1 /*
   2  * Copyright (c) 2006, 2013, 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  * @test
  26  * @bug 6503247 6574123
  27  * @summary Test resilience to tryAcquire methods that throw
  28  * @author Martin Buchholz
  29  */
  30 
  31 import java.util.concurrent.ThreadLocalRandom;
  32 import java.util.concurrent.CyclicBarrier;
  33 import java.util.concurrent.ExecutorService;
  34 import java.util.concurrent.Executors;
  35 import java.util.concurrent.TimeUnit;
  36 import java.util.concurrent.locks.AbstractQueuedLongSynchronizer;
  37 import java.util.concurrent.locks.Condition;
  38 import java.util.concurrent.locks.Lock;
  39 
  40 /**
  41  * This uses a variant of the standard Mutex demo, except with a
  42  * tryAcquire method that randomly throws various Throwable
  43  * subclasses.
  44  */
  45 @SuppressWarnings("serial")
  46 public class FlakyMutex implements Lock {
  47     static class MyError extends Error {}
  48     static class MyException extends Exception {}
  49     static class MyRuntimeException extends RuntimeException {}
  50 
  51     static void checkThrowable(Throwable t) {
  52         if (!((t instanceof MyError) ||
  53               (t instanceof MyException) ||
  54               (t instanceof MyRuntimeException)))
  55             unexpected(t);
  56     }
  57 
  58     static void realMain(String[] args) throws Throwable {
  59         final ThreadLocalRandom rndMain = ThreadLocalRandom.current();
  60         final int nCpus = Runtime.getRuntime().availableProcessors();
  61         final int maxThreads = Math.min(4, nCpus);
  62         final int nThreads = rndMain.nextInt(1, maxThreads + 1);
  63         final int iterations = 10_000;
  64         final CyclicBarrier startingGate = new CyclicBarrier(nThreads);
  65         final ExecutorService es = Executors.newFixedThreadPool(nThreads);
  66         final FlakyMutex mutex = new FlakyMutex();
  67         final Runnable task = () -> {
  68             try {
  69                 ThreadLocalRandom rnd = ThreadLocalRandom.current();
  70                 startingGate.await();
  71                 for (int i = 0; i < iterations; i++) {
  72                     for (;;) {
  73                         try {
  74                             if (rnd.nextBoolean())
  75                                 mutex.lock();
  76                             else
  77                                 mutex.lockInterruptibly();
  78                             break;
  79                         } catch (Throwable t) { checkThrowable(t); }
  80                     }
  81 
  82                     if (rnd.nextBoolean()) {
  83                         try {
  84                             check(! mutex.tryLock());
  85                         } catch (Throwable t) { checkThrowable(t); }
  86                     }
  87 
  88                     if (rnd.nextInt(10) == 0) {
  89                         try {
  90                             check(! mutex.tryLock(1, TimeUnit.MICROSECONDS));
  91                         } catch (Throwable t) { checkThrowable(t); }
  92                     }
  93 
  94                     if (rnd.nextBoolean()) {
  95                         check(mutex.isLocked());
  96                     }
  97 
  98                     mutex.unlock();
  99                 }
 100             } catch (Throwable t) { unexpected(t); }
 101         };
 102 
 103         for (int i = 0; i < nThreads; i++)
 104             es.submit(task);
 105         es.shutdown();
 106         // Let test harness handle timeout
 107         check(es.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS));
 108     }
 109 
 110     private static class FlakySync extends AbstractQueuedLongSynchronizer {
 111         private static final long serialVersionUID = -1L;
 112 
 113         public boolean isHeldExclusively() { return getState() == 1; }
 114 
 115         public boolean tryAcquire(long acquires) {
 116             // Sneak in some tests for queue state
 117             if (hasQueuedPredecessors())
 118                 check(getFirstQueuedThread() != Thread.currentThread());
 119             if (getFirstQueuedThread() == Thread.currentThread()) {
 120                 check(hasQueuedThreads());
 121                 check(!hasQueuedPredecessors());
 122             } else {
 123                 // Might be true, but only transiently
 124                 do {} while (hasQueuedPredecessors() != hasQueuedThreads());
 125             }
 126 
 127             switch (ThreadLocalRandom.current().nextInt(10)) {
 128             case 0: throw new MyError();
 129             case 1: throw new MyRuntimeException();
 130             case 2: FlakyMutex.<RuntimeException>uncheckedThrow(new MyException());
 131                 // fall through ... NOT!
 132             default: return compareAndSetState(0, 1);
 133             }
 134         }
 135 
 136         public boolean tryRelease(long releases) {
 137             setState(0);
 138             return true;
 139         }
 140 
 141         Condition newCondition() { return new ConditionObject(); }
 142     }
 143 
 144     private final FlakySync sync = new FlakySync();
 145     public void lock() { sync.acquire(1); }
 146     public boolean tryLock() { return sync.tryAcquire(1); }
 147     public void lockInterruptibly() throws InterruptedException {
 148         sync.acquireInterruptibly(1);
 149     }
 150     public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
 151         return sync.tryAcquireNanos(1, unit.toNanos(timeout));
 152     }
 153     public void unlock() { sync.release(1); }
 154     public Condition newCondition()   { return sync.newCondition(); }
 155     public boolean isLocked()         { return sync.isHeldExclusively(); }
 156     public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
 157 
 158     //--------------------- Infrastructure ---------------------------
 159     static volatile int passed = 0, failed = 0;
 160     static void pass() {passed++;}
 161     static void fail() {failed++; Thread.dumpStack();}
 162     static void fail(String msg) {System.out.println(msg); fail();}
 163     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
 164     static void check(boolean cond) {if (cond) pass(); else fail();}
 165     static void equal(Object x, Object y) {
 166         if (x == null ? y == null : x.equals(y)) pass();
 167         else fail(x + " not equal to " + y);}
 168     public static void main(String[] args) throws Throwable {
 169         int runsPerTest = Integer.getInteger("jsr166.runsPerTest", 1);
 170         try {
 171             for (int i = runsPerTest; i--> 0; )
 172                 realMain(args);
 173         } catch (Throwable t) { unexpected(t); }
 174         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
 175         if (failed > 0) throw new AssertionError("Some tests failed");}
 176     @SuppressWarnings("unchecked")
 177     static <T extends Throwable> void uncheckedThrow(Throwable t) throws T {
 178         throw (T)t; // rely on vacuous cast
 179     }
 180 }