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