1 /* 2 * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 /* 24 */ 25 package org.graalvm.compiler.core.test.deopt; 26 27 import jdk.vm.ci.code.InstalledCode; 28 import jdk.vm.ci.meta.ResolvedJavaMethod; 29 30 import org.junit.Assert; 31 import org.junit.Test; 32 33 import org.graalvm.compiler.code.CompilationResult; 34 import org.graalvm.compiler.core.test.GraalCompilerTest; 35 import org.graalvm.compiler.nodes.AbstractEndNode; 36 import org.graalvm.compiler.nodes.FixedNode; 37 import org.graalvm.compiler.nodes.FixedWithNextNode; 38 import org.graalvm.compiler.nodes.LoopBeginNode; 39 import org.graalvm.compiler.nodes.StructuredGraph; 40 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; 41 42 public final class MonitorDeoptTest extends GraalCompilerTest { 43 44 private enum State { 45 INITIAL, 46 ALLOWING_SAFEPOINT, 47 RUNNING_GRAAL, 48 INVALIDATED, 49 RUNNING_INTERPRETER, 50 TERMINATED 51 } 52 53 static final long TIMEOUT = 5000; 54 55 private static class Monitor { 56 private volatile State state = State.INITIAL; 57 58 public synchronized void setState(State newState) { 59 state = newState; 60 notifyAll(); 61 } 62 63 public synchronized boolean tryUpdateState(State oldState, State newState) { 64 if (state == oldState) { 65 state = newState; 66 notifyAll(); 67 return true; 68 } else { 69 return false; 70 } 71 } 72 73 public synchronized void waitState(State targetState) throws InterruptedException { 74 long startTime = System.currentTimeMillis(); 75 while (state != targetState && System.currentTimeMillis() - startTime < TIMEOUT) { 76 wait(100); 77 } 78 if (state != targetState) { 79 throw new IllegalStateException("Timed out waiting for " + targetState); 80 } 81 } 82 83 /** 84 * Change the current state to {@link State#ALLOWING_SAFEPOINT} and do a short wait to allow 85 * a safepoint to happen. Then restore the state to the original value. 86 * 87 * @param expectedState 88 * @throws InterruptedException 89 */ 90 public synchronized void safepoint(State expectedState) throws InterruptedException { 91 if (state == expectedState) { 92 state = State.ALLOWING_SAFEPOINT; 93 wait(1); 94 if (state != State.ALLOWING_SAFEPOINT) { 95 throw new InternalError("Other threads can not update the state from ALLOWING_SAFEPOINT: " + state); 96 } 97 state = expectedState; 98 notifyAll(); 99 } 100 } 101 102 public synchronized State getState() { 103 return state; 104 } 105 106 public synchronized void invalidate(InstalledCode code) throws InterruptedException { 107 // wait for the main thread to start 108 waitState(State.RUNNING_GRAAL); 109 110 state = State.INVALIDATED; 111 code.invalidate(); 112 } 113 } 114 115 public static boolean test(Monitor monitor) throws InterruptedException { 116 // initially, we're running as Graal compiled code 117 monitor.setState(State.RUNNING_GRAAL); 118 119 boolean timedOut = true; 120 long startTime = System.currentTimeMillis(); 121 long safepointCheckTime = startTime; 122 while (System.currentTimeMillis() - startTime < TIMEOUT) { 123 // wait for the compiled code to be invalidated 124 if (monitor.tryUpdateState(State.INVALIDATED, State.RUNNING_INTERPRETER)) { 125 timedOut = false; 126 break; 127 } 128 if (System.currentTimeMillis() - safepointCheckTime > 200) { 129 /* 130 * It's possible for a safepoint to be triggered by external code before 131 * invalidation is ready. Allow a safepoint to occur if required but don't allow 132 * invalidation to proceed. 133 */ 134 monitor.safepoint(State.RUNNING_GRAAL); 135 safepointCheckTime = System.currentTimeMillis(); 136 } 137 } 138 if (timedOut) { 139 throw new InternalError("Timed out while waiting for code to be invalidated: " + monitor.getState()); 140 } 141 142 for (int i = 0; i < 500; i++) { 143 // wait for the control thread to send the TERMINATED signal 144 if (monitor.getState() == State.TERMINATED) { 145 return true; 146 } 147 148 try { 149 Thread.sleep(10); 150 } catch (InterruptedException e) { 151 } 152 } 153 154 // we're running for more than 5 s in the interpreter 155 // probably the control thread is deadlocked 156 return false; 157 } 158 159 private static LoopBeginNode findFirstLoop(StructuredGraph graph) { 160 FixedNode node = graph.start(); 161 for (;;) { 162 if (node instanceof LoopBeginNode) { 163 return (LoopBeginNode) node; 164 } else if (node instanceof FixedWithNextNode) { 165 node = ((FixedWithNextNode) node).next(); 166 } else if (node instanceof AbstractEndNode) { 167 node = ((AbstractEndNode) node).merge(); 168 } else { 169 Assert.fail(String.format("unexpected node %s in graph of test method", node)); 170 } 171 } 172 } 173 174 /** 175 * Remove the safepoint from the first loop in the test method, so only the safepoints on 176 * MonitorEnter and MonitorExit remain in the loop. That way, we can make sure it deopts inside 177 * the MonitorEnter by invalidating the code while holding the lock. 178 */ 179 private static void removeLoopSafepoint(StructuredGraph graph) { 180 LoopBeginNode loopBegin = findFirstLoop(graph); 181 loopBegin.disableSafepoint(); 182 } 183 184 @Test 185 public void run0() throws Throwable { 186 ResolvedJavaMethod javaMethod = getResolvedJavaMethod("test"); 187 188 StructuredGraph graph = parseEager(javaMethod, AllowAssumptions.YES); 189 removeLoopSafepoint(graph); 190 191 CompilationResult compilationResult = compile(javaMethod, graph); 192 final InstalledCode installedCode = getBackend().createDefaultInstalledCode(javaMethod, compilationResult); 193 194 final Monitor monitor = new Monitor(); 195 196 Thread controlThread = new Thread(new Runnable() { 197 198 @Override 199 public void run() { 200 try { 201 // Wait for thread to reach RUNNING_GRAAL and then invalidate the code 202 monitor.invalidate(installedCode); 203 204 // wait for the main thread to continue running in the interpreter 205 monitor.waitState(State.RUNNING_INTERPRETER); 206 207 // terminate the main thread 208 monitor.setState(State.TERMINATED); 209 } catch (InterruptedException e) { 210 } 211 } 212 }); 213 214 controlThread.start(); 215 216 boolean result = test(monitor); 217 Assert.assertTrue(result); 218 } 219 }