/* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/licenses/publicdomain * Other contributors include John Vint */ import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.*; import junit.framework.Test; import junit.framework.TestSuite; public class PhaserTest extends JSR166TestCase { public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } public static Test suite() { return new TestSuite(PhaserTest.class); } /** * Empty constructor builds a new Phaser with no parent, no registered * parties and initial phase number of 0 */ public void testConstructor1() { Phaser phaser = new Phaser(); assertNull(phaser.getParent()); assertEquals(0, phaser.getArrivedParties()); assertEquals(0, phaser.getPhase()); } /** * A negative party number for the constructor throws illegal argument * exception */ public void testConstructor2() { try { new Phaser(-1); this.shouldThrow(); } catch (IllegalArgumentException success) { } } /** * The parent being input into the constructor should equal the original * parent when being returned */ public void testConstructor3() { Phaser parent = new Phaser(); assertEquals(parent, new Phaser(parent).getParent()); } /** * A negative party number for the constructor throws illegal argument * exception */ public void testConstructor4() { try { new Phaser(new Phaser(), -1); shouldThrow(); } catch (IllegalArgumentException success) { } } /** * The parent being input into the parameter should equal the original * parent when being returned */ public void testConstructor5() { Phaser parent = new Phaser(); assertEquals(parent, new Phaser(parent, 0).getParent()); } /** * register() will increment the number of unarrived parties by one and not * affect its arrived parties */ public void testRegister1() { Phaser phaser = new Phaser(); assertEquals(0, phaser.getUnarrivedParties()); phaser.register(); assertEquals(1, phaser.getUnarrivedParties()); assertEquals(0, phaser.getArrivedParties()); } /** * Registering more than 65536 parties causes IllegalStateException */ public void testRegister2() { Phaser phaser = new Phaser(0); int expectedUnnarivedParties = (1 << 16) - 1; for (int i = 0; i < expectedUnnarivedParties; i++) { phaser.register(); assertEquals(i + 1, phaser.getUnarrivedParties()); } try { phaser.register(); shouldThrow(); } catch (IllegalStateException success) { } catch (Exception ex) { threadUnexpectedException(ex); } } /** * register() correctly returns the current barrier phase number when * invoked */ public void testRegister3() { Phaser phaser = new Phaser(); assertEquals(0, phaser.register()); phaser.arrive(); assertEquals(1, phaser.register()); } /** * register causes the next arrive to not increment the phase rather retain * the phase number */ public void testRegister4() { Phaser phaser = new Phaser(1); phaser.arrive(); int expectedPhase = phaser.register(); phaser.arrive(); assertEquals(expectedPhase, phaser.getPhase()); } public void testRegister5() { Phaser phaser = new Phaser(); phaser.register(); assertEquals(1, phaser.getUnarrivedParties()); } /** * Invoking bulkRegister with a negative parameter throws an * IllegalArgumentException */ public void testBulkRegister1() { try { new Phaser().bulkRegister(-1); shouldThrow(); } catch (IllegalArgumentException success) { } } /** * bulkRegister should correctly record the number of unarrived parties with * the number of parties being registered */ public void testBulkRegister2() { Phaser phaser = new Phaser(); phaser.bulkRegister(20); assertEquals(20, phaser.getUnarrivedParties()); } /** * Registering with a number of parties greater than or equal to 1<<16 * throws IllegalStateException. */ public void testBulkRegister3() { try { new Phaser().bulkRegister(1 << 16); shouldThrow(); } catch (IllegalStateException success) { } } /** * the phase number increments correctly when tripping the barrier */ public void testPhaseIncrement1() { for (int size = 1; size < nine; size++) { final Phaser phaser = new Phaser(size); for (int index = 0; index <= (1 << size); index++) { int phase = phaser.arrive(); assertTrue(index % size == 0 ? (index / size) == phase : index - (phase * size) > 0); } } } /** * Arrive() on a registered phaser increments phase. */ public void testArrive1() { Phaser phaser = new Phaser(1); phaser.arrive(); assertEquals(1, phaser.getPhase()); } /** * arrive does not wait for others to arrive at barrier */ public void testArrive2() { final Phaser phaser = new Phaser(1); phaser.register(); Thread thread = null; for (final Runnable r : getRunnables(10, SHORT_DELAY_MS)) { phaser.register(); thread = new Thread() { public void run() { r.run(); phaser.arriveAndDeregister(); } }; thread.start(); } phaser.arrive(); assertTrue(thread.isAlive()); assertFalse(phaser.isTerminated()); } /** * arrive() returns a negative number if the Phaser is terminated */ public void testArrive3() { Phaser phaser = new Phaser(1); phaser.forceTermination(); assertTrue(phaser.arrive() < 0); } /** * arriveAndDeregister() throws IllegalStateException if number of * registered or unarrived parties would become negative */ public void testArriveAndDeregister1() { try { Phaser phaser = new Phaser(); phaser.arriveAndDeregister(); shouldThrow(); } catch (IllegalStateException success) { } } /** * arriveAndDeregister deregisters reduces the number of arrived parties */ public void testArriveAndDergeister2() { final Phaser phaser = new Phaser(1); phaser.register(); phaser.arrive(); int p = phaser.getArrivedParties(); assertTrue(p == 1); phaser.arriveAndDeregister(); assertTrue(phaser.getArrivedParties() < p); } /** * arriveAndDeregister arrives to the barrier on a phaser with a parent and * when a deregistration occurs and causes the phaser to have zero parties * its parent will be deregistered as well */ public void testArriveAndDeregsiter3() { Phaser parent = new Phaser(); Phaser root = new Phaser(parent); root.register(); assertTrue(parent.getUnarrivedParties() > 0); assertTrue(root.getUnarrivedParties() > 0); root.arriveAndDeregister(); assertTrue(parent.getUnarrivedParties() == 0); assertTrue(root.getUnarrivedParties() == 0); assertTrue(root.isTerminated() && parent.isTerminated()); } /** * arriveAndDeregister deregisters one party from its parent when * the number of parties of root is zero after deregistration */ public void testArriveAndDeregsiter4() { Phaser parent = new Phaser(); Phaser root = new Phaser(parent); parent.register(); root.register(); int parentParties = parent.getUnarrivedParties(); root.arriveAndDeregister(); assertEquals(parentParties - 1, parent.getUnarrivedParties()); } /** * arriveAndDeregister deregisters one party from its parent when * the number of parties of root is nonzero after deregistration. */ public void testArriveAndDeregister5() { Phaser parent = new Phaser(); Phaser child = new Phaser(parent); Phaser root = new Phaser(child); assertTrue(parent.getUnarrivedParties() > 0); assertTrue(child.getUnarrivedParties() > 0); root.register(); root.arriveAndDeregister(); assertTrue(parent.getUnarrivedParties() == 0); assertTrue(child.getUnarrivedParties() == 0); assertTrue(root.isTerminated()); } /** * arriveAndDeregister returns the phase in which it leaves the * phaser in after deregistration */ public void testArriveAndDeregister6() { final Phaser phaser = new Phaser(2); new Thread() { public void run() { getRunnable(SHORT_DELAY_MS).run(); phaser.arrive(); } }.start(); phaser.arriveAndAwaitAdvance(); int phase = phaser.arriveAndDeregister(); assertEquals(phase, phaser.getPhase()); } /** * awaitAdvance succeeds upon advance */ public void testAwaitAdvance1() { final Phaser phaser = new Phaser(1); phaser.awaitAdvance(phaser.arrive()); } /** * awaitAdvance with a negative parameter will return without affecting the * phaser */ public void testAwaitAdvance2() { try { Phaser phaser = new Phaser(); phaser.awaitAdvance(-1); } catch (Exception failure) { this.unexpectedException(); } } /** * awaitAdvance while waiting does not abort on interrupt. */ public void testAwaitAdvance3() { final Phaser phaser = new Phaser(); Thread th1 = new Thread() { public void run() { try { phaser.register(); getRunnable(LONG_DELAY_MS).run(); phaser.awaitAdvance(phaser.arrive()); } catch (Exception failure) { threadUnexpectedException(failure); } } }; phaser.register(); th1.start(); try { Thread.sleep(SHORT_DELAY_MS); th1.interrupt(); Thread.sleep(LONG_DELAY_MS); phaser.arrive(); } catch (Exception failure) { unexpectedException(); } assertFalse(th1.isInterrupted()); } /** * awaitAdvance atomically waits for all parties within the same phase to * complete before continuing */ public void testAwaitAdvance4() { final Phaser phaser = new Phaser(four); final AtomicInteger phaseCount = new AtomicInteger(0); for (int i = 0; i < four; i++) { new Thread() { public void run() { int phase = phaser.arrive(); phaseCount.incrementAndGet(); getRunnable(LONG_DELAY_MS).run(); phaser.awaitAdvance(phase); assertTrue(phaseCount.get() == four); } }.start(); } } /** * awaitAdvance returns the current phase */ public void testAwaitAdvance5() { final Phaser phaser = new Phaser(1); int phase = phaser.awaitAdvance(phaser.arrive()); assertEquals(phase, phaser.getPhase()); phaser.register(); for (int i = 0; i < eight; i++) { new Thread() { public void run() { getRunnable(SHORT_DELAY_MS).run(); phaser.arrive(); } }.start(); phase = phaser.awaitAdvance(phaser.arrive()); assertEquals(phase, phaser.getPhase()); } } /** * awaitAdvance returns when the phaser is externally terminated */ public void testAwaitAdvance6() { final Phaser phaser = new Phaser(3); /* * Start new thread. This thread waits a small amount of time * and waits for the other two parties to arrive. The party * in the main thread arrives quickly so at best this thread * waits for the second thread's party to arrive */ new Thread() { public void run() { getRunnable(SMALL_DELAY_MS).run(); int phase = phaser.awaitAdvance(phaser.arrive()); /* * This point is reached when force termination is called in which phase = -1 */ threadAssertTrue(phase < 0); threadAssertTrue(phaser.isTerminated()); } }.start(); /* * This thread will cause the first thread run to wait, in doing so * the main thread will force termination in which the first thread * should exit peacefully as this one */ new Thread() { public void run() { getRunnable(LONG_DELAY_MS).run(); int p1 = phaser.arrive(); int phase = phaser.awaitAdvance(p1); threadAssertTrue(phase < 0); threadAssertTrue(phaser.isTerminated()); } }.start(); phaser.arrive(); phaser.forceTermination(); } /** * arriveAndAwaitAdvance throws IllegalStateException with no * unarrived parties */ public void testArriveAndAwaitAdvance1() { try { Phaser phaser = new Phaser(); phaser.arriveAndAwaitAdvance(); shouldThrow(); } catch (IllegalStateException success) { } } /** * Interrupted arriveAndAwaitAdvance does not throw InterruptedException */ public void testArriveAndAwaitAdvance2() { final Phaser phaser = new Phaser(2); Thread th = new Thread() { public void run() { try { phaser.arriveAndAwaitAdvance(); } catch (Exception failure) { threadUnexpectedException(failure); } } }; try { th.start(); Thread.sleep(LONG_DELAY_MS); th.interrupt(); Thread.sleep(LONG_DELAY_MS); phaser.arrive(); } catch (InterruptedException failure) { this.unexpectedException(); } assertFalse(th.isInterrupted()); } /** * arriveAndAwaitAdvance waits for all threads to arrive, the * number of arrived parties is the same number that is accounted * for when the main thread awaitsAdvance */ public void testArriveAndAwaitAdvance3() { final Phaser phaser = new Phaser(1); final AtomicInteger arrivingCount = new AtomicInteger(0); for (final Runnable run : getRunnables(six, SHORT_DELAY_MS)) { new Thread() { public void run() { phaser.register(); run.run(); arrivingCount.getAndIncrement(); phaser.arrive(); } }.start(); } int phaseNumber = phaser.arriveAndAwaitAdvance(); arrivingCount.incrementAndGet(); //the + 1 adds to expectedArrive to account for the main threads arrival int expectedArrived = phaseNumber > 0 ? phaseNumber * six + 1 : phaser.getArrivedParties() + 1; threadAssertEquals(expectedArrived, arrivingCount.get()); } // .. initially called, for n tasks via private List getRunnables(int size, long wait) { List list = new ArrayList(); for (int i = 0; i < size; i++) { list.add(getRunnable(wait)); } return list; } private Runnable getRunnable(final long wait) { return new Runnable() { public void run() { try { Thread.sleep(wait); } catch (InterruptedException noop) { // sleep interruption isn't a problem case for these example } catch (Exception ex) { threadUnexpectedException(ex); } } }; } }