1 /*
   2  * Copyright (c) 2005, 2014, 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 6253848 6366811
  27  * @summary Basic tests for CyclicBarrier
  28  * @library /test/lib
  29  * @author Martin Buchholz, David Holmes
  30  */
  31 
  32 import static java.util.concurrent.TimeUnit.MILLISECONDS;
  33 
  34 import java.util.ArrayList;
  35 import java.util.Iterator;
  36 import java.util.List;
  37 import java.util.concurrent.BrokenBarrierException;
  38 import java.util.concurrent.CountDownLatch;
  39 import java.util.concurrent.CyclicBarrier;
  40 import java.util.concurrent.TimeoutException;
  41 import java.util.concurrent.atomic.AtomicInteger;
  42 import jdk.test.lib.Utils;
  43 
  44 public class Basic {
  45     static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
  46 
  47     private static void checkBroken(final CyclicBarrier barrier) {
  48         check(barrier.isBroken());
  49         equal(barrier.getNumberWaiting(), 0);
  50 
  51         THROWS(BrokenBarrierException.class,
  52                () -> barrier.await(),
  53                () -> barrier.await(100, MILLISECONDS));
  54     }
  55 
  56     private static void reset(CyclicBarrier barrier) {
  57         barrier.reset();
  58         check(! barrier.isBroken());
  59         equal(barrier.getNumberWaiting(), 0);
  60     }
  61 
  62     private static void checkResult(Awaiter a, Class<? extends Throwable> c) {
  63         Throwable t = a.result();
  64         if (! ((t == null && c == null) || (c != null && c.isInstance(t)))) {
  65             //      t.printStackTrace();
  66             fail("Mismatch in thread " +
  67                  a.getName() + ": " +
  68                  t + ", " +
  69                  (c == null ? "<null>" : c.getName()));
  70         } else {
  71             pass();
  72         }
  73     }
  74 
  75     //----------------------------------------------------------------
  76     // Mechanism to get all victim threads into "running" mode.
  77     // The fact that this also uses CyclicBarrier is entirely coincidental.
  78     //----------------------------------------------------------------
  79     private static final CyclicBarrier atTheStartingGate = new CyclicBarrier(3);
  80 
  81     private static void toTheStartingGate() {
  82         try { atTheStartingGate.await(LONG_DELAY_MS, MILLISECONDS); pass(); }
  83         catch (Throwable t) {
  84             unexpected(t);
  85             reset(atTheStartingGate);
  86             throw new Error(t);
  87         }
  88     }
  89 
  90     //----------------------------------------------------------------
  91     // Convenience methods for creating threads that call CyclicBarrier.await
  92     //----------------------------------------------------------------
  93     private abstract static class Awaiter extends Thread {
  94         static AtomicInteger count = new AtomicInteger(1);
  95 
  96         {
  97             this.setName("Awaiter:"+count.getAndIncrement());
  98             this.setDaemon(true);
  99         }
 100 
 101         private volatile Throwable result = null;
 102         protected void result(Throwable result) { this.result = result; }
 103         public Throwable result() { return this.result; }
 104     }
 105 
 106     private static Awaiter awaiter(final CyclicBarrier barrier) {
 107         return new Awaiter() { public void run() {
 108             toTheStartingGate();
 109 
 110             try { barrier.await(); }
 111             catch (Throwable result) { result(result); }}};
 112     }
 113 
 114     private static Awaiter awaiter(final CyclicBarrier barrier,
 115                                    final long millis) {
 116         return new Awaiter() { public void run() {
 117             toTheStartingGate();
 118 
 119             try { barrier.await(millis, MILLISECONDS); }
 120             catch (Throwable result) { result(result); }}};
 121     }
 122 
 123     // Returns an infinite lazy list of all possible awaiter pair combinations.
 124     private static Iterator<Awaiter> awaiterIterator(final CyclicBarrier barrier) {
 125         return new Iterator<Awaiter>() {
 126             int i = 0;
 127             public boolean hasNext() { return true; }
 128             public Awaiter next() {
 129                 switch ((i++)&7) {
 130                 case 0: case 2: case 4: case 5:
 131                     return awaiter(barrier);
 132                 default:
 133                     return awaiter(barrier, 10 * 1000); }}
 134             public void remove() {throw new UnsupportedOperationException();}};
 135     }
 136 
 137     private static void realMain(String[] args) throws Throwable {
 138 
 139         Thread.currentThread().setName("mainThread");
 140 
 141         //----------------------------------------------------------------
 142         // Normal use
 143         //----------------------------------------------------------------
 144         try {
 145             CyclicBarrier barrier = new CyclicBarrier(3);
 146             equal(barrier.getParties(), 3);
 147             Iterator<Awaiter> awaiters = awaiterIterator(barrier);
 148             for (boolean doReset : new boolean[] {false, true})
 149                 for (int i = 0; i < 4; i++) {
 150                     Awaiter a1 = awaiters.next(); a1.start();
 151                     Awaiter a2 = awaiters.next(); a2.start();
 152                     toTheStartingGate();
 153                     barrier.await();
 154                     a1.join();
 155                     a2.join();
 156                     checkResult(a1, null);
 157                     checkResult(a2, null);
 158                     check(! barrier.isBroken());
 159                     equal(barrier.getParties(), 3);
 160                     equal(barrier.getNumberWaiting(), 0);
 161                     if (doReset) reset(barrier);
 162                 }
 163         } catch (Throwable t) { unexpected(t); }
 164 
 165         //----------------------------------------------------------------
 166         // One thread interrupted
 167         //----------------------------------------------------------------
 168         try {
 169             CyclicBarrier barrier = new CyclicBarrier(3);
 170             Iterator<Awaiter> awaiters = awaiterIterator(barrier);
 171             for (int i = 0; i < 4; i++) {
 172                 Awaiter a1 = awaiters.next(); a1.start();
 173                 Awaiter a2 = awaiters.next(); a2.start();
 174                 toTheStartingGate();
 175                 a1.interrupt();
 176                 a1.join();
 177                 a2.join();
 178                 checkResult(a1, InterruptedException.class);
 179                 checkResult(a2, BrokenBarrierException.class);
 180                 checkBroken(barrier);
 181                 reset(barrier);
 182             }
 183         } catch (Throwable t) { unexpected(t); }
 184 
 185         //----------------------------------------------------------------
 186         // Barrier is reset while threads are waiting
 187         //----------------------------------------------------------------
 188         try {
 189             CyclicBarrier barrier = new CyclicBarrier(3);
 190             Iterator<Awaiter> awaiters = awaiterIterator(barrier);
 191             for (int i = 0; i < 4; i++) {
 192                 Awaiter a1 = awaiters.next(); a1.start();
 193                 Awaiter a2 = awaiters.next(); a2.start();
 194                 toTheStartingGate();
 195                 while (barrier.getNumberWaiting() < 2) Thread.yield();
 196                 barrier.reset();
 197                 a1.join();
 198                 a2.join();
 199                 checkResult(a1, BrokenBarrierException.class);
 200                 checkResult(a2, BrokenBarrierException.class);
 201                 check(! barrier.isBroken());
 202                 equal(barrier.getParties(), 3);
 203                 equal(barrier.getNumberWaiting(), 0);
 204             }
 205         } catch (Throwable t) { unexpected(t); }
 206 
 207         //----------------------------------------------------------------
 208         // One thread timed out
 209         //----------------------------------------------------------------
 210         try {
 211             CyclicBarrier barrier = new CyclicBarrier(3);
 212             Iterator<Awaiter> awaiters = awaiterIterator(barrier);
 213             for (long timeout : new long[] { 0L, 10L }) {
 214                 for (int i = 0; i < 2; i++) {
 215                     Awaiter a1 = awaiter(barrier, timeout); a1.start();
 216                     Awaiter a2 = awaiters.next();           a2.start();
 217                     toTheStartingGate();
 218                     a1.join();
 219                     a2.join();
 220                     checkResult(a1, TimeoutException.class);
 221                     checkResult(a2, BrokenBarrierException.class);
 222                     checkBroken(barrier);
 223                     equal(barrier.getParties(), 3);
 224                     reset(barrier);
 225                 }
 226             }
 227         } catch (Throwable t) { unexpected(t); }
 228 
 229         //----------------------------------------------------------------
 230         // Barrier action completed normally
 231         //----------------------------------------------------------------
 232         try {
 233             final AtomicInteger count = new AtomicInteger(0);
 234             final CyclicBarrier[] kludge = new CyclicBarrier[1];
 235             Runnable action = new Runnable() { public void run() {
 236                 count.incrementAndGet();
 237                 equal(kludge[0].getNumberWaiting(),
 238                       kludge[0].getParties());
 239                 System.out.println("OK!"); }};
 240             CyclicBarrier barrier = new CyclicBarrier(3, action);
 241             kludge[0] = barrier;
 242             equal(barrier.getParties(), 3);
 243             Iterator<Awaiter> awaiters = awaiterIterator(barrier);
 244             for (int i = 0; i < 4; i++) {
 245                 Awaiter a1 = awaiters.next(); a1.start();
 246                 Awaiter a2 = awaiters.next(); a2.start();
 247                 toTheStartingGate();
 248                 while (barrier.getNumberWaiting() < 2) Thread.yield();
 249                 try { barrier.await(); }
 250                 catch (Throwable t) { unexpected(t); }
 251                 a1.join();
 252                 a2.join();
 253                 checkResult(a1, null);
 254                 checkResult(a2, null);
 255                 check(! barrier.isBroken());
 256                 equal(barrier.getNumberWaiting(), 0);
 257                 reset(barrier);
 258                 equal(count.get(), i+1);
 259             }
 260         } catch (Throwable t) { unexpected(t); }
 261 
 262         //----------------------------------------------------------------
 263         // Barrier action threw exception
 264         //----------------------------------------------------------------
 265         try {
 266             Runnable action = new Runnable() {
 267                     public void run() { throw new Error(); }};
 268             CyclicBarrier barrier = new CyclicBarrier(3, action);
 269             Iterator<Awaiter> awaiters = awaiterIterator(barrier);
 270             for (int i = 0; i < 4; i++) {
 271                 Awaiter a1 = awaiters.next(); a1.start();
 272                 Awaiter a2 = awaiters.next(); a2.start();
 273                 toTheStartingGate();
 274                 while (barrier.getNumberWaiting() < 2) Thread.yield();
 275                 try {
 276                     barrier.await();
 277                     fail("Expected Error not thrown"); }
 278                 catch (Error e) { pass(); }
 279                 catch (Throwable t) { unexpected(t); }
 280                 a1.join();
 281                 a2.join();
 282                 checkResult(a1, BrokenBarrierException.class);
 283                 checkResult(a2, BrokenBarrierException.class);
 284                 checkBroken(barrier);
 285                 reset(barrier);
 286             }
 287         } catch (Throwable t) { unexpected(t); }
 288 
 289         testInterrupts();
 290     }
 291 
 292     /**
 293      * Handling of extra interrupts while waiting - tests for bug 6366811
 294      */
 295     private static void testInterrupts() {
 296         final int N = 10;
 297         final CyclicBarrier startingGate = new CyclicBarrier(N+1);
 298 
 299         /**
 300          * A version of Awaiter that also records interrupted state.
 301          */
 302         class Waiter extends CheckedThread {
 303             private boolean timed;
 304             private CyclicBarrier barrier;
 305             private CountDownLatch doneSignal;
 306             private Throwable throwable;
 307             private boolean interrupted;
 308 
 309             public Waiter(boolean timed,
 310                           CountDownLatch doneSignal,
 311                           CyclicBarrier barrier) {
 312                 this.timed = timed;
 313                 this.doneSignal = doneSignal;
 314                 this.barrier = barrier;
 315             }
 316             Throwable throwable() { return this.throwable; }
 317             boolean interruptBit() { return this.interrupted; }
 318             void realRun() throws Throwable {
 319                 startingGate.await(LONG_DELAY_MS, MILLISECONDS);
 320                 try {
 321                     if (timed) barrier.await(LONG_DELAY_MS, MILLISECONDS);
 322                     else barrier.await(); }
 323                 catch (Throwable throwable) { this.throwable = throwable; }
 324 
 325                 try { doneSignal.await(LONG_DELAY_MS, MILLISECONDS); }
 326                 catch (InterruptedException e) { interrupted = true; }
 327             }
 328         }
 329 
 330         //----------------------------------------------------------------
 331         // Interrupt occurs during barrier trip
 332         //----------------------------------------------------------------
 333         try {
 334             final CountDownLatch doneSignal = new CountDownLatch(1);
 335             final List<Waiter> waiters = new ArrayList<>(N);
 336 
 337             // work around finality of closed-over variables
 338             final Runnable[] realAction = new Runnable[1];
 339             final Runnable delegateAction =
 340                 new Runnable() {public void run() {realAction[0].run();}};
 341             final CyclicBarrier barrier = new CyclicBarrier(N+1, delegateAction);
 342 
 343             realAction[0] = new Runnable() { public void run() {
 344                 try {
 345                     for (int i = 0; i < N/2; i++)
 346                         waiters.get(i).interrupt();
 347                     // we need to try and ensure that the waiters get
 348                     // to process their interruption before we do the
 349                     // signalAll that trips the barrier. Using sleep
 350                     // seems to work reliably while yield does not.
 351                     Thread.sleep(100);
 352                 } catch (Throwable t) { unexpected(t); }
 353             }};
 354             for (int i = 0; i < N; i++) {
 355                 Waiter waiter = new Waiter(i < N/2, doneSignal, barrier);
 356                 waiter.start();
 357                 waiters.add(waiter);
 358             }
 359             startingGate.await(LONG_DELAY_MS, MILLISECONDS);
 360             while (barrier.getNumberWaiting() < N) Thread.yield();
 361             barrier.await();
 362             doneSignal.countDown();
 363             int countInterrupted = 0;
 364             int countInterruptedException = 0;
 365             int countBrokenBarrierException = 0;
 366             for (Waiter waiter : waiters) {
 367                 waiter.join();
 368                 equal(waiter.throwable(), null);
 369                 if (waiter.interruptBit())
 370                     countInterrupted++;
 371             }
 372             equal(countInterrupted, N/2);
 373             check(! barrier.isBroken());
 374         } catch (Throwable t) { unexpected(t); }
 375 
 376         //----------------------------------------------------------------
 377         // Multiple interrupts occur during barrier await
 378         //----------------------------------------------------------------
 379         try {
 380             final CountDownLatch doneSignal = new CountDownLatch(1);
 381             final CyclicBarrier barrier = new CyclicBarrier(N+1);
 382             final List<Waiter> waiters = new ArrayList<>(N);
 383             for (int i = 0; i < N; i++) {
 384                 Waiter waiter = new Waiter(i < N/2, doneSignal, barrier);
 385                 waiter.start();
 386                 waiters.add(waiter);
 387             }
 388             startingGate.await(LONG_DELAY_MS, MILLISECONDS);
 389             while (barrier.getNumberWaiting() < N) Thread.yield();
 390             for (int i = 0; i < N/2; i++)
 391                 waiters.get(i).interrupt();
 392             doneSignal.countDown();
 393             int countInterrupted = 0;
 394             int countInterruptedException = 0;
 395             int countBrokenBarrierException = 0;
 396             for (Waiter waiter : waiters) {
 397                 waiter.join();
 398                 if (waiter.throwable() instanceof InterruptedException)
 399                     countInterruptedException++;
 400                 if (waiter.throwable() instanceof BrokenBarrierException)
 401                     countBrokenBarrierException++;
 402                 if (waiter.interruptBit())
 403                     countInterrupted++;
 404             }
 405             equal(countInterrupted, N/2-1);
 406             equal(countInterruptedException, 1);
 407             equal(countBrokenBarrierException, N-1);
 408             checkBroken(barrier);
 409             reset(barrier);
 410         } catch (Throwable t) { unexpected(t); }
 411     }
 412 
 413     //--------------------- Infrastructure ---------------------------
 414     static volatile int passed = 0, failed = 0;
 415     static void pass() {passed++;}
 416     static void fail() {failed++; Thread.dumpStack();}
 417     static void fail(String msg) {System.out.println(msg); fail();}
 418     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
 419     static void check(boolean cond) {if (cond) pass(); else fail();}
 420     static void equal(Object x, Object y) {
 421         if (x == null ? y == null : x.equals(y)) pass();
 422         else fail(x + " not equal to " + y);}
 423     public static void main(String[] args) throws Throwable {
 424         try {realMain(args);} catch (Throwable t) {unexpected(t);}
 425         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
 426         if (failed > 0) throw new AssertionError("Some tests failed");}
 427     interface Fun {void f() throws Throwable;}
 428     private static void THROWS(Class<? extends Throwable> k, Fun... fs) {
 429         for (Fun f : fs)
 430             try { f.f(); fail("Expected " + k.getName() + " not thrown"); }
 431             catch (Throwable t) {
 432                 if (k.isAssignableFrom(t.getClass())) pass();
 433                 else unexpected(t);}}
 434     private abstract static class CheckedThread extends Thread {
 435         abstract void realRun() throws Throwable;
 436         public void run() {
 437             try {realRun();} catch (Throwable t) {unexpected(t);}}}
 438 }