1 /*
   2  * Copyright (c) 2006, 2012, 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.*;
  32 import java.util.concurrent.*;
  33 import java.util.concurrent.locks.*;
  34 import java.lang.reflect.Field;
  35 import sun.misc.Unsafe;
  36 
  37 /**
  38  * This uses a variant of the standard Mutex demo, except with a
  39  * tryAcquire method that randomly throws various Throwable
  40  * subclasses.
  41  */
  42 @SuppressWarnings({"deprecation", "serial"})
  43 public class FlakyMutex implements Lock {
  44     private static final Unsafe UNSAFE;
  45     static {
  46         try {
  47             Field f = Unsafe.class.getDeclaredField("theUnsafe");
  48             f.setAccessible(true);
  49             UNSAFE = (Unsafe)f.get(null);
  50         } catch (NoSuchFieldException | IllegalAccessException e) {
  51             throw new RuntimeException(e);
  52         }
  53     }
  54 
  55     static class MyError extends Error {}
  56     static class MyException extends Exception {}
  57     static class MyRuntimeException extends RuntimeException {}
  58 
  59     static final Random rnd = new Random();
  60 
  61     static void maybeThrow() {
  62         switch (rnd.nextInt(10)) {
  63         case 0: throw new MyError();
  64         case 1: throw new MyRuntimeException();
  65         case 2: UNSAFE.throwException(new MyException()); break;
  66         default: /* Do nothing */ break;
  67         }
  68     }
  69 
  70     static void checkThrowable(Throwable t) {
  71         check((t instanceof MyError) ||
  72               (t instanceof MyException) ||
  73               (t instanceof MyRuntimeException));
  74     }
  75 
  76     static void realMain(String[] args) throws Throwable {
  77         final int nThreads = 3;
  78         final CyclicBarrier barrier = new CyclicBarrier(nThreads + 1);
  79         final FlakyMutex m = new FlakyMutex();
  80         final ExecutorService es = Executors.newFixedThreadPool(nThreads);
  81         for (int i = 0; i < nThreads; i++) {
  82             es.submit(new Runnable() { public void run() {
  83                 try {
  84                     barrier.await();
  85                     for (int i = 0; i < 10000; i++) {
  86                         for (;;) {
  87                             try { m.lock(); break; }
  88                             catch (Throwable t) { checkThrowable(t); }
  89                         }
  90 
  91                         try { check(! m.tryLock()); }
  92                         catch (Throwable t) { checkThrowable(t); }
  93 
  94                         try { check(! m.tryLock(1, TimeUnit.MICROSECONDS)); }
  95                         catch (Throwable t) { checkThrowable(t); }
  96 
  97                         m.unlock();
  98                     }
  99                 } catch (Throwable t) { unexpected(t); }}});}
 100         barrier.await();
 101         es.shutdown();
 102         check(es.awaitTermination(30, TimeUnit.SECONDS));
 103     }
 104 
 105     private static class FlakySync extends AbstractQueuedLongSynchronizer {
 106         private static final long serialVersionUID = -1L;
 107 
 108         public boolean isHeldExclusively() { return getState() == 1; }
 109 
 110         public boolean tryAcquire(long acquires) {
 111             // Sneak in some tests for queue state
 112             if (hasQueuedPredecessors())
 113                 check(getFirstQueuedThread() != Thread.currentThread());
 114             if (getFirstQueuedThread() == Thread.currentThread()) {
 115                 check(hasQueuedThreads());
 116                 check(!hasQueuedPredecessors());
 117             } else {
 118                 // Might be true, but only transiently
 119                 do {} while (hasQueuedPredecessors() != hasQueuedThreads());
 120             }
 121 
 122             maybeThrow();
 123             return compareAndSetState(0, 1);
 124         }
 125 
 126         public boolean tryRelease(long releases) {
 127             setState(0);
 128             return true;
 129         }
 130 
 131         Condition newCondition() { return new ConditionObject(); }
 132     }
 133 
 134     private final FlakySync sync = new FlakySync();
 135     public void lock() { sync.acquire(1); }
 136     public boolean tryLock() { return sync.tryAcquire(1); }
 137     public void lockInterruptibly() throws InterruptedException {
 138         sync.acquireInterruptibly(1);
 139     }
 140     public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
 141         return sync.tryAcquireNanos(1, unit.toNanos(timeout));
 142     }
 143     public void unlock() { sync.release(1); }
 144     public Condition newCondition()   { return sync.newCondition(); }
 145     public boolean isLocked()         { return sync.isHeldExclusively(); }
 146     public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
 147 
 148     //--------------------- Infrastructure ---------------------------
 149     static volatile int passed = 0, failed = 0;
 150     static void pass() {passed++;}
 151     static void fail() {failed++; Thread.dumpStack();}
 152     static void fail(String msg) {System.out.println(msg); fail();}
 153     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
 154     static void check(boolean cond) {if (cond) pass(); else fail();}
 155     static void equal(Object x, Object y) {
 156         if (x == null ? y == null : x.equals(y)) pass();
 157         else fail(x + " not equal to " + y);}
 158     public static void main(String[] args) throws Throwable {
 159         try {realMain(args);} catch (Throwable t) {unexpected(t);}
 160         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
 161         if (failed > 0) throw new AssertionError("Some tests failed");}
 162 }