001/*
002 * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023/*
024 */
025package com.oracle.graal.compiler.test.deopt;
026
027import jdk.internal.jvmci.code.*;
028import jdk.internal.jvmci.meta.*;
029
030import org.junit.*;
031
032import com.oracle.graal.compiler.test.*;
033import com.oracle.graal.nodes.*;
034import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
035
036public final class MonitorDeoptTest extends GraalCompilerTest {
037
038    private enum State {
039        INITIAL,
040        RUNNING_GRAAL,
041        INVALIDATED,
042        RUNNING_INTERPRETER,
043        TERMINATED
044    }
045
046    private static class Monitor {
047        private volatile State state = State.INITIAL;
048
049        public synchronized void setState(State newState) {
050            state = newState;
051            notifyAll();
052        }
053
054        public synchronized boolean tryUpdateState(State oldState, State newState) {
055            if (state == oldState) {
056                state = newState;
057                notifyAll();
058                return true;
059            } else {
060                return false;
061            }
062        }
063
064        public synchronized void waitState(State targetState) throws InterruptedException {
065            while (state != targetState) {
066                wait();
067            }
068        }
069
070        public synchronized State getState() {
071            return state;
072        }
073
074        public synchronized void invalidate(InstalledCode code) {
075            state = State.INVALIDATED;
076            code.invalidate();
077        }
078    }
079
080    public static boolean test(Monitor monitor) {
081        // initially, we're running as Graal compiled code
082        monitor.setState(State.RUNNING_GRAAL);
083
084        for (;;) {
085            // wait for the compiled code to be invalidated
086            if (monitor.tryUpdateState(State.INVALIDATED, State.RUNNING_INTERPRETER)) {
087                break;
088            }
089        }
090
091        for (int i = 0; i < 500; i++) {
092            // wait for the control thread to send the TERMINATED signal
093            if (monitor.getState() == State.TERMINATED) {
094                return true;
095            }
096
097            try {
098                Thread.sleep(10);
099            } catch (InterruptedException e) {
100            }
101        }
102
103        // we're running for more than 5 s in the interpreter
104        // probably the control thread is deadlocked
105        return false;
106    }
107
108    private static LoopBeginNode findFirstLoop(StructuredGraph graph) {
109        FixedNode node = graph.start();
110        for (;;) {
111            if (node instanceof LoopBeginNode) {
112                return (LoopBeginNode) node;
113            } else if (node instanceof FixedWithNextNode) {
114                node = ((FixedWithNextNode) node).next();
115            } else if (node instanceof AbstractEndNode) {
116                node = ((AbstractEndNode) node).merge();
117            } else {
118                Assert.fail(String.format("unexpected node %s in graph of test method", node));
119            }
120        }
121    }
122
123    /**
124     * Remove the safepoint from the first loop in the test method, so only the safepoints on
125     * MonitorEnter and MonitorExit remain in the loop. That way, we can make sure it deopts inside
126     * the MonitorEnter by invalidating the code while holding the lock.
127     */
128    private static void removeLoopSafepoint(StructuredGraph graph) {
129        LoopBeginNode loopBegin = findFirstLoop(graph);
130        for (LoopEndNode end : loopBegin.loopEnds()) {
131            end.disableSafepoint();
132        }
133    }
134
135    @Test
136    public void run0() throws Throwable {
137        ResolvedJavaMethod javaMethod = getResolvedJavaMethod("test");
138
139        StructuredGraph graph = parseEager(javaMethod, AllowAssumptions.YES);
140        removeLoopSafepoint(graph);
141
142        CompilationResult compilationResult = compile(javaMethod, graph);
143        final InstalledCode installedCode = getProviders().getCodeCache().setDefaultMethod(javaMethod, compilationResult);
144
145        final Monitor monitor = new Monitor();
146
147        Thread controlThread = new Thread(new Runnable() {
148
149            public void run() {
150                try {
151                    // wait for the main thread to start
152                    monitor.waitState(State.RUNNING_GRAAL);
153
154                    // invalidate the compiled code while holding the lock
155                    // at this point, the compiled method hangs in a MonitorEnter
156                    monitor.invalidate(installedCode);
157
158                    // wait for the main thread to continue running in the interpreter
159                    monitor.waitState(State.RUNNING_INTERPRETER);
160
161                    // terminate the main thread
162                    monitor.setState(State.TERMINATED);
163                } catch (InterruptedException e) {
164                }
165            }
166        });
167
168        controlThread.start();
169
170        boolean result = test(monitor);
171        Assert.assertTrue(result);
172    }
173}