1 /* 2 * Copyright (c) 2011, 2018, 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 # int THREADS = 999; 26 # if ( THREADS % 3 != 0 ) throw new RuntimeException("THREADS should be a multiple of 3!"); 27 # 28 package vm.mlvm.mixed.stress.java.findDeadlock; 29 30 import java.lang.invoke.CallSite; 31 import java.lang.invoke.MethodHandle; 32 import java.lang.invoke.MethodHandles; 33 import java.lang.invoke.MethodType; 34 import java.lang.invoke.MutableCallSite; 35 import java.lang.management.ManagementFactory; 36 import java.lang.management.ThreadMXBean; 37 import java.lang.reflect.Method; 38 import java.util.concurrent.CyclicBarrier; 39 import java.util.concurrent.locks.ReentrantLock; 40 41 import nsk.share.test.Stresser; 42 import vm.mlvm.share.Env; 43 import vm.mlvm.share.MlvmTest; 44 45 public class INDIFY_Test extends MlvmTest { 46 47 public static final int THREAD_NUM = @THREADS; 48 public static final int ITERATIONS = 1000; 49 50 static ThreadMXBean _threadMXBean = ManagementFactory.getThreadMXBean(); 51 52 static Thread[] _threads = new Thread[THREAD_NUM]; 53 static ReentrantLock[] _locks = new ReentrantLock[THREAD_NUM]; 54 static MethodHandle[] _mh = new MethodHandle[THREAD_NUM]; 55 static MutableCallSite[] _cs = new MutableCallSite[THREAD_NUM]; 56 57 static CyclicBarrier _threadRaceStartBarrier; 58 static volatile boolean _testFailed; 59 static volatile boolean _testDone; 60 static volatile int _iteration; 61 62 private static int nextLock(int n) { return (n + 1) % THREAD_NUM; } 63 64 private static boolean lock(String place, int n, boolean lockInterruptible) throws Throwable { 65 boolean locked = false; 66 place = Thread.currentThread().getName() + ": " + place; 67 if ( ! lockInterruptible ) { 68 Env.traceVerbose(place + ": Locking " + n); 69 _locks[n].lock(); 70 locked = true; 71 } else { 72 try { 73 Env.traceVerbose(place + ": Locking interruptibly " + n); 74 _locks[n].lockInterruptibly(); 75 locked = true; 76 77 if ( ! _testDone ) 78 throw new Exception(place + ": LOCKED " + n); 79 else 80 Env.traceVerbose(place + ": LOCKED " + n); 81 82 } catch ( InterruptedException swallow ) { 83 Env.traceVerbose(place + ": interrupted while locking " + n); 84 } 85 } 86 87 return locked; 88 } 89 90 private static boolean unlock(String place, int n) throws Throwable { 91 place = Thread.currentThread().getName() + ": " + place; 92 Env.traceVerbose(place + ": Unlocking " + n); 93 _locks[n].unlock(); 94 Env.traceVerbose(place + ": UNLOCKED " + n); 95 return false; 96 } 97 98 static Object bsmt(int lockNum, Object l, Object n, Object m) throws Throwable { 99 DeadlockedThread thread = (DeadlockedThread) Thread.currentThread(); 100 101 if ( l instanceof MethodHandles.Lookup ) { 102 // Method is used as BSM 103 Env.traceVerbose(thread.getName() + ": Entered BSM. Lock=" + lockNum); 104 105 if ( _iteration > 0 ) 106 throw new Exception("BSM called twice!"); 107 108 switch ( lockNum % 3 ) { 109 case 0: 110 thread._lockedCurrent = lock("BSM", lockNum, false); 111 _threadRaceStartBarrier.await(); 112 thread._lockedNext = lock("BSM", nextLock(lockNum), true); 113 break; 114 115 case 1: 116 thread._lockedCurrent = lock("BSM", lockNum, false); 117 break; 118 119 case 2: 120 // Do everything in target method 121 break; 122 } 123 124 return (_cs[lockNum] = new MutableCallSite(_mh[lockNum])); 125 126 } else { 127 // Method is used as target 128 Env.traceVerbose(thread.getName() + ": Entered target method. Lock=" + lockNum); 129 130 try { 131 if ( _iteration > 0 ) { 132 133 switch ( lockNum % 3 ) { 134 case 0: 135 thread._lockedCurrent = lock("Target", lockNum, false); 136 _threadRaceStartBarrier.await(); 137 thread._lockedNext = lock("Target", nextLock(lockNum), true); 138 break; 139 140 case 1: 141 thread._lockedCurrent = lock("Target", lockNum, false); 142 _threadRaceStartBarrier.await(); 143 Env.traceVerbose(thread.getName() + ": Entering synchronize ( " + lockNum + " )"); 144 synchronized ( _locks[nextLock(lockNum)] ) { 145 } 146 Env.traceVerbose(thread.getName() + ": Exited synchronize ( " + lockNum + " )"); 147 break; 148 149 case 2: 150 Env.traceVerbose(thread.getName() + ": Entering synchronize ( " + lockNum + " )"); 151 synchronized ( _locks[lockNum] ) { 152 _threadRaceStartBarrier.await(); 153 thread._lockedNext = lock("Target", nextLock(lockNum), true); 154 thread._lockedNext = unlock("Target", nextLock(lockNum)); 155 } 156 Env.traceVerbose(thread.getName() + ": Exited synchronize ( " + lockNum + " )"); 157 break; 158 } 159 160 } else { 161 switch ( lockNum % 3 ) { 162 case 0: 163 // Everything is done in BSM 164 break; 165 166 case 1: 167 _threadRaceStartBarrier.await(); 168 thread._lockedNext = lock("Target", nextLock(lockNum), true); 169 break; 170 171 case 2: 172 thread._lockedCurrent = lock("Target", lockNum, false); 173 _threadRaceStartBarrier.await(); 174 thread._lockedNext = lock("Target", nextLock(lockNum), true); 175 break; 176 } 177 178 } 179 180 return null; 181 } finally { 182 if ( thread._lockedNext ) 183 thread._lockedNext = unlock("Target", nextLock(lockNum)); 184 if ( thread._lockedCurrent ) 185 thread._lockedCurrent = unlock("Target", lockNum); 186 } 187 } 188 } 189 190 // BSM + Indy pairs 191 # 192 # for (int i = 0; i < THREADS; i++ ) { 193 # String MT_bootstrap = "MT_bootstrap" + i; 194 # String MH_bootstrap = "MH_bootstrap" + i; 195 # String INDY_call = "INDY_call" + i; 196 # String bootstrap = "bootstrap" + i; 197 # String qBootstrap = "\"bootstrap" + i + "\""; 198 # String indyWrapper = "indyWrapper" + i; 199 # 200 // @i 201 private static MethodType @MT_bootstrap () { return MethodType.methodType(Object.class, Object.class, Object.class, Object.class); } 202 203 private static MethodHandle @MH_bootstrap () throws Exception { 204 return MethodHandles.lookup().findStatic(INDIFY_Test.class, @qBootstrap, @MT_bootstrap ()); 205 } 206 207 private static MethodHandle @INDY_call; 208 private static MethodHandle @INDY_call () throws Throwable { 209 if (@INDY_call != null) return @INDY_call; 210 CallSite cs = (CallSite) @MH_bootstrap ().invokeWithArguments(MethodHandles.lookup(), "gimmeTarget", @MT_bootstrap ()); 211 return cs.dynamicInvoker(); 212 } 213 214 static Object @indyWrapper (Object o1, Object o2, Object o3) throws Throwable { return @INDY_call ().invokeExact(o1, o2, o3); } 215 216 static Object @bootstrap (Object l, Object n, Object t) throws Throwable { return _mh[ @i ].invokeExact(l, n, t); } 217 218 # 219 # } 220 # 221 222 // End of BSM+indy pairs 223 224 public boolean run() throws Throwable { 225 226 if ( ! _threadMXBean.isSynchronizerUsageSupported() ) { 227 Env.getLog().complain("Platform does not detect deadlocks in synchronizers. Please exclude this test on this platform."); 228 return false; 229 } 230 231 MethodHandle bsmt = MethodHandles.lookup().findStatic( 232 getClass(), "bsmt", MethodType.methodType(Object.class, int.class, Object.class, Object.class, Object.class)); 233 234 for ( int i = 0; i < THREAD_NUM; i++ ) 235 _mh[i] = MethodHandles.insertArguments(bsmt, 0, i); 236 237 for ( int i = 0; i < THREAD_NUM; i++ ) 238 _locks[i] = new ReentrantLock(); 239 240 Stresser stresser = new Stresser(Env.getArgParser().getArguments()); 241 stresser.start(ITERATIONS); 242 try { 243 _iteration = 0; 244 while ( stresser.iteration() ) { 245 if ( ! test() ) { 246 return false; 247 } 248 _iteration++; 249 } 250 } finally { 251 stresser.finish(); 252 } 253 254 return true; 255 } 256 257 boolean test() throws Throwable { 258 Env.traceNormal("Starting test..."); 259 260 for ( int i = 0; i < THREAD_NUM; i++ ) { 261 if ( _locks[i].isLocked() ) { 262 Env.getLog().complain("Lock " + i + " is still locked!"); 263 _testFailed = true; 264 } 265 } 266 267 if ( _testFailed ) 268 throw new Exception("Some locks are still locked"); 269 270 _threadRaceStartBarrier = new CyclicBarrier(THREAD_NUM + 1); 271 _testDone = false; 272 _testFailed = false; 273 274 for ( int i = 0; i < THREAD_NUM; i++ ) 275 (_threads[i] = new DeadlockedThread(i)).start(); 276 277 try { 278 _threadRaceStartBarrier.await(); 279 Env.traceVerbose("Start race..."); 280 281 // 282 // Wait for the deadlock and detect it using ThreadMXBean 283 // 284 285 boolean resultsReady = false; 286 for ( int i = 0; i < 10 && ! resultsReady && ! _testFailed; i++ ) { 287 Env.traceNormal("Waiting for threads to lock up..."); 288 Thread.sleep(100); 289 290 resultsReady = true; 291 for ( int t = 0; t < THREAD_NUM; t++ ) { 292 if ( _iteration == 0 && t % 3 != 2 && ! _locks[t].hasQueuedThreads() ) { 293 Env.traceVerbose("Lock " + t + ": no waiters"); 294 resultsReady = false; 295 } else { 296 Env.traceVerbose("Lock " + t + ": has waiters"); 297 } 298 } 299 } 300 301 if ( ! resultsReady ) 302 Env.traceImportant("Warning: threads are still not deadlocked?"); 303 304 long[] deadlockedThreads = _threadMXBean.findDeadlockedThreads(); 305 if ( deadlockedThreads == null ) { 306 Env.complain("Found no deadlocked threads. Expected to find " + THREAD_NUM); 307 return false; 308 } else if ( deadlockedThreads.length != THREAD_NUM ) { 309 Env.complain("Found " + deadlockedThreads.length + " deadlocked threads. Expected to find " + THREAD_NUM); 310 return false; 311 } else { 312 Env.traceNormal("Found " + deadlockedThreads.length + " deadlocked threads as expected"); 313 return ! _testFailed; 314 } 315 } finally { 316 _testDone = true; 317 318 _threads[0].interrupt(); 319 320 for ( int i = 0; i < THREAD_NUM; i++ ) { 321 _threads[i].join(1000); 322 if ( _threads[i].isAlive() ) 323 Env.getLog().complain("Thread " + _threads[i].getName() + " is still alive"); 324 } 325 326 MutableCallSite.syncAll(_cs); 327 } 328 } 329 330 static class DeadlockedThread extends Thread { 331 int _n; 332 boolean _lockedCurrent = false; 333 boolean _lockedNext = false; 334 335 public DeadlockedThread(int n) { 336 super(); 337 setDaemon(true); 338 _n = n; 339 } 340 341 public void run() { 342 try { 343 Method m = INDIFY_Test.class.getDeclaredMethod("indyWrapper" + _n, Object.class, Object.class, Object.class); 344 m.invoke(null, new Object(), new Object(), _n); 345 } catch ( Throwable t ) { 346 Env.getLog().complain("Exception in thread " + getName()); 347 t.printStackTrace(Env.getLog().getOutStream()); 348 _testFailed = true; 349 } 350 } 351 } 352 353 public static void main(String[] args) { MlvmTest.launch(args); } 354 }