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 }