--- jsr166/src/test/tck/SemaphoreTest.java 2003/09/20 18:20:08 1.4 +++ jsr166/src/test/tck/SemaphoreTest.java 2018/01/23 20:44:11 1.43 @@ -1,245 +1,651 @@ /* - * Written by members of JCP JSR-166 Expert Group and released to the - * public domain. Use, modify, and redistribute this code in any way - * without acknowledgement. Other contributors include Andrew Wright, - * Jeffrey Hayes, Pat Fischer, Mike Judd. + * 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/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. */ -import junit.framework.*; -import java.util.*; -import java.util.concurrent.*; -import java.io.*; +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.Collection; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Semaphore; +import java.util.concurrent.ThreadLocalRandom; + +import junit.framework.Test; +import junit.framework.TestSuite; public class SemaphoreTest extends JSR166TestCase { public static void main(String[] args) { - junit.textui.TestRunner.run (suite()); + main(suite(), args); } public static Test suite() { - return new TestSuite(SemaphoreTest.class); + return new TestSuite(SemaphoreTest.class); } /** - * + * Subclass to expose protected methods */ - public void testConstructor1() { - Semaphore s = new Semaphore(0); - assertEquals(0, s.availablePermits()); + static class PublicSemaphore extends Semaphore { + PublicSemaphore(int permits) { super(permits); } + PublicSemaphore(int permits, boolean fair) { super(permits, fair); } + public Collection getQueuedThreads() { + return super.getQueuedThreads(); + } + public boolean hasQueuedThread(Thread t) { + return super.getQueuedThreads().contains(t); + } + public void reducePermits(int reduction) { + super.reducePermits(reduction); + } } /** - * + * A runnable calling acquire */ - public void testConstructor2() { - Semaphore s = new Semaphore(-1); - assertEquals(-1, s.availablePermits()); + class InterruptibleLockRunnable extends CheckedRunnable { + final Semaphore lock; + InterruptibleLockRunnable(Semaphore s) { lock = s; } + public void realRun() { + try { + lock.acquire(); + } + catch (InterruptedException ignored) {} + } + } + + /** + * A runnable calling acquire that expects to be interrupted + */ + class InterruptedLockRunnable extends CheckedInterruptedRunnable { + final Semaphore lock; + InterruptedLockRunnable(Semaphore s) { lock = s; } + public void realRun() throws InterruptedException { + lock.acquire(); + } + } + + /** + * Spin-waits until s.hasQueuedThread(t) becomes true. + */ + void waitForQueuedThread(PublicSemaphore s, Thread t) { + long startTime = System.nanoTime(); + while (!s.hasQueuedThread(t)) { + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + throw new AssertionError("timed out"); + Thread.yield(); + } + assertTrue(s.hasQueuedThreads()); + assertTrue(t.isAlive()); + } + + /** + * Spin-waits until s.hasQueuedThreads() becomes true. + */ + void waitForQueuedThreads(Semaphore s) { + long startTime = System.nanoTime(); + while (!s.hasQueuedThreads()) { + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + throw new AssertionError("timed out"); + Thread.yield(); + } + } + + enum AcquireMethod { + acquire() { + void acquire(Semaphore s) throws InterruptedException { + s.acquire(); + } + }, + acquireN() { + void acquire(Semaphore s, int permits) throws InterruptedException { + s.acquire(permits); + } + }, + acquireUninterruptibly() { + void acquire(Semaphore s) { + s.acquireUninterruptibly(); + } + }, + acquireUninterruptiblyN() { + void acquire(Semaphore s, int permits) { + s.acquireUninterruptibly(permits); + } + }, + tryAcquire() { + void acquire(Semaphore s) { + assertTrue(s.tryAcquire()); + } + }, + tryAcquireN() { + void acquire(Semaphore s, int permits) { + assertTrue(s.tryAcquire(permits)); + } + }, + tryAcquireTimed() { + void acquire(Semaphore s) throws InterruptedException { + assertTrue(s.tryAcquire(2 * LONG_DELAY_MS, MILLISECONDS)); + } + Thread.State parkedState() { return Thread.State.TIMED_WAITING; } + }, + tryAcquireTimedN { + void acquire(Semaphore s, int permits) throws InterruptedException { + assertTrue(s.tryAcquire(permits, 2 * LONG_DELAY_MS, MILLISECONDS)); + } + Thread.State parkedState() { return Thread.State.TIMED_WAITING; } + }; + + // Intentionally meta-circular + + /** Acquires 1 permit. */ + void acquire(Semaphore s) throws InterruptedException { + acquire(s, 1); + } + /** Acquires the given number of permits. */ + void acquire(Semaphore s, int permits) throws InterruptedException { + for (int i = 0; i < permits; i++) + acquire(s); + } + Thread.State parkedState() { return Thread.State.WAITING; } } /** - * + * Zero, negative, and positive initial values are allowed in constructor */ - public void testTryAcquireInSameThread() { - Semaphore s = new Semaphore(2); + public void testConstructor() { testConstructor(false); } + public void testConstructor_fair() { testConstructor(true); } + public void testConstructor(boolean fair) { + for (int permits : new int[] { -42, -1, 0, 1, 42 }) { + Semaphore s = new Semaphore(permits, fair); + assertEquals(permits, s.availablePermits()); + assertEquals(fair, s.isFair()); + } + } + + /** + * Constructor without fairness argument behaves as nonfair + */ + public void testConstructorDefaultsToNonFair() { + for (int permits : new int[] { -42, -1, 0, 1, 42 }) { + Semaphore s = new Semaphore(permits); + assertEquals(permits, s.availablePermits()); + assertFalse(s.isFair()); + } + } + + /** + * tryAcquire succeeds when sufficient permits, else fails + */ + public void testTryAcquireInSameThread() { testTryAcquireInSameThread(false); } + public void testTryAcquireInSameThread_fair() { testTryAcquireInSameThread(true); } + public void testTryAcquireInSameThread(boolean fair) { + Semaphore s = new Semaphore(2, fair); assertEquals(2, s.availablePermits()); assertTrue(s.tryAcquire()); assertTrue(s.tryAcquire()); assertEquals(0, s.availablePermits()); assertFalse(s.tryAcquire()); + assertFalse(s.tryAcquire()); + assertEquals(0, s.availablePermits()); } /** - * + * timed tryAcquire times out */ - public void testAcquireReleaseInSameThread() { - Semaphore s = new Semaphore(1); - try { - s.acquire(); - s.release(); - s.acquire(); - s.release(); - s.acquire(); - s.release(); - s.acquire(); - s.release(); - s.acquire(); - s.release(); - assertEquals(1, s.availablePermits()); - } catch( InterruptedException e){ - unexpectedException(); + public void testTryAcquire_timeout() { + final boolean fair = ThreadLocalRandom.current().nextBoolean(); + final Semaphore s = new Semaphore(0, fair); + final long startTime = System.nanoTime(); + try { assertFalse(s.tryAcquire(timeoutMillis(), MILLISECONDS)); } + catch (InterruptedException e) { threadUnexpectedException(e); } + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + } + + /** + * timed tryAcquire(N) times out + */ + public void testTryAcquireN_timeout() { + final boolean fair = ThreadLocalRandom.current().nextBoolean(); + final Semaphore s = new Semaphore(2, fair); + final long startTime = System.nanoTime(); + try { assertFalse(s.tryAcquire(3, timeoutMillis(), MILLISECONDS)); } + catch (InterruptedException e) { threadUnexpectedException(e); } + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + } + + /** + * acquire(), acquire(N), timed tryAcquired, timed tryAcquire(N) + * are interruptible + */ + public void testInterruptible_acquire() { testInterruptible(false, AcquireMethod.acquire); } + public void testInterruptible_acquire_fair() { testInterruptible(true, AcquireMethod.acquire); } + public void testInterruptible_acquireN() { testInterruptible(false, AcquireMethod.acquireN); } + public void testInterruptible_acquireN_fair() { testInterruptible(true, AcquireMethod.acquireN); } + public void testInterruptible_tryAcquireTimed() { testInterruptible(false, AcquireMethod.tryAcquireTimed); } + public void testInterruptible_tryAcquireTimed_fair() { testInterruptible(true, AcquireMethod.tryAcquireTimed); } + public void testInterruptible_tryAcquireTimedN() { testInterruptible(false, AcquireMethod.tryAcquireTimedN); } + public void testInterruptible_tryAcquireTimedN_fair() { testInterruptible(true, AcquireMethod.tryAcquireTimedN); } + public void testInterruptible(boolean fair, final AcquireMethod acquirer) { + final PublicSemaphore s = new PublicSemaphore(0, fair); + final java.util.concurrent.CyclicBarrier pleaseInterrupt + = new java.util.concurrent.CyclicBarrier(2); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + // Interrupt before acquire + Thread.currentThread().interrupt(); + try { + acquirer.acquire(s); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + // Interrupt before acquire(N) + Thread.currentThread().interrupt(); + try { + acquirer.acquire(s, 3); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + // Interrupt during acquire + await(pleaseInterrupt); + try { + acquirer.acquire(s); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + // Interrupt during acquire(N) + await(pleaseInterrupt); + try { + acquirer.acquire(s, 3); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + for (int n = 2; n-->0; ) { + await(pleaseInterrupt); + assertThreadBlocks(t, acquirer.parkedState()); + t.interrupt(); } + + awaitTermination(t); + } + + /** + * acquireUninterruptibly(), acquireUninterruptibly(N) are + * uninterruptible + */ + public void testUninterruptible_acquireUninterruptibly() { testUninterruptible(false, AcquireMethod.acquireUninterruptibly); } + public void testUninterruptible_acquireUninterruptibly_fair() { testUninterruptible(true, AcquireMethod.acquireUninterruptibly); } + public void testUninterruptible_acquireUninterruptiblyN() { testUninterruptible(false, AcquireMethod.acquireUninterruptiblyN); } + public void testUninterruptible_acquireUninterruptiblyN_fair() { testUninterruptible(true, AcquireMethod.acquireUninterruptiblyN); } + public void testUninterruptible(boolean fair, final AcquireMethod acquirer) { + final PublicSemaphore s = new PublicSemaphore(0, fair); + final Semaphore pleaseInterrupt = new Semaphore(-1, fair); + + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + // Interrupt before acquire + pleaseInterrupt.release(); + Thread.currentThread().interrupt(); + acquirer.acquire(s); + assertTrue(Thread.interrupted()); + }}); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + // Interrupt during acquire + pleaseInterrupt.release(); + acquirer.acquire(s); + assertTrue(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + waitForQueuedThread(s, t1); + waitForQueuedThread(s, t2); + t2.interrupt(); + + assertThreadBlocks(t1, Thread.State.WAITING); + assertThreadBlocks(t2, Thread.State.WAITING); + + s.release(2); + + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * hasQueuedThreads reports whether there are waiting threads + */ + public void testHasQueuedThreads() { testHasQueuedThreads(false); } + public void testHasQueuedThreads_fair() { testHasQueuedThreads(true); } + public void testHasQueuedThreads(boolean fair) { + final PublicSemaphore lock = new PublicSemaphore(1, fair); + assertFalse(lock.hasQueuedThreads()); + lock.acquireUninterruptibly(); + Thread t1 = newStartedThread(new InterruptedLockRunnable(lock)); + waitForQueuedThread(lock, t1); + assertTrue(lock.hasQueuedThreads()); + Thread t2 = newStartedThread(new InterruptibleLockRunnable(lock)); + waitForQueuedThread(lock, t2); + assertTrue(lock.hasQueuedThreads()); + t1.interrupt(); + awaitTermination(t1); + assertTrue(lock.hasQueuedThreads()); + lock.release(); + awaitTermination(t2); + assertFalse(lock.hasQueuedThreads()); + } + + /** + * getQueueLength reports number of waiting threads + */ + public void testGetQueueLength() { testGetQueueLength(false); } + public void testGetQueueLength_fair() { testGetQueueLength(true); } + public void testGetQueueLength(boolean fair) { + final PublicSemaphore lock = new PublicSemaphore(1, fair); + assertEquals(0, lock.getQueueLength()); + lock.acquireUninterruptibly(); + Thread t1 = newStartedThread(new InterruptedLockRunnable(lock)); + waitForQueuedThread(lock, t1); + assertEquals(1, lock.getQueueLength()); + Thread t2 = newStartedThread(new InterruptibleLockRunnable(lock)); + waitForQueuedThread(lock, t2); + assertEquals(2, lock.getQueueLength()); + t1.interrupt(); + awaitTermination(t1); + assertEquals(1, lock.getQueueLength()); + lock.release(); + awaitTermination(t2); + assertEquals(0, lock.getQueueLength()); + } + + /** + * getQueuedThreads includes waiting threads + */ + public void testGetQueuedThreads() { testGetQueuedThreads(false); } + public void testGetQueuedThreads_fair() { testGetQueuedThreads(true); } + public void testGetQueuedThreads(boolean fair) { + final PublicSemaphore lock = new PublicSemaphore(1, fair); + assertTrue(lock.getQueuedThreads().isEmpty()); + lock.acquireUninterruptibly(); + assertTrue(lock.getQueuedThreads().isEmpty()); + Thread t1 = newStartedThread(new InterruptedLockRunnable(lock)); + waitForQueuedThread(lock, t1); + assertTrue(lock.getQueuedThreads().contains(t1)); + Thread t2 = newStartedThread(new InterruptibleLockRunnable(lock)); + waitForQueuedThread(lock, t2); + assertTrue(lock.getQueuedThreads().contains(t1)); + assertTrue(lock.getQueuedThreads().contains(t2)); + t1.interrupt(); + awaitTermination(t1); + assertFalse(lock.getQueuedThreads().contains(t1)); + assertTrue(lock.getQueuedThreads().contains(t2)); + lock.release(); + awaitTermination(t2); + assertTrue(lock.getQueuedThreads().isEmpty()); + } + + /** + * drainPermits reports and removes given number of permits + */ + public void testDrainPermits() { testDrainPermits(false); } + public void testDrainPermits_fair() { testDrainPermits(true); } + public void testDrainPermits(boolean fair) { + Semaphore s = new Semaphore(0, fair); + assertEquals(0, s.availablePermits()); + assertEquals(0, s.drainPermits()); + s.release(10); + assertEquals(10, s.availablePermits()); + assertEquals(10, s.drainPermits()); + assertEquals(0, s.availablePermits()); + assertEquals(0, s.drainPermits()); } /** - * + * release(-N) throws IllegalArgumentException */ - public void testAcquireUninterruptiblyReleaseInSameThread() { - Semaphore s = new Semaphore(1); + public void testReleaseIAE() { testReleaseIAE(false); } + public void testReleaseIAE_fair() { testReleaseIAE(true); } + public void testReleaseIAE(boolean fair) { + Semaphore s = new Semaphore(10, fair); try { - s.acquireUninterruptibly(); - s.release(); - s.acquireUninterruptibly(); - s.release(); - s.acquireUninterruptibly(); - s.release(); - s.acquireUninterruptibly(); - s.release(); - s.acquireUninterruptibly(); - s.release(); - assertEquals(1, s.availablePermits()); - } finally { - } + s.release(-1); + shouldThrow(); + } catch (IllegalArgumentException success) {} } + /** + * reducePermits(-N) throws IllegalArgumentException + */ + public void testReducePermitsIAE() { testReducePermitsIAE(false); } + public void testReducePermitsIAE_fair() { testReducePermitsIAE(true); } + public void testReducePermitsIAE(boolean fair) { + PublicSemaphore s = new PublicSemaphore(10, fair); + try { + s.reducePermits(-1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } /** - * + * reducePermits reduces number of permits + */ + public void testReducePermits() { testReducePermits(false); } + public void testReducePermits_fair() { testReducePermits(true); } + public void testReducePermits(boolean fair) { + PublicSemaphore s = new PublicSemaphore(10, fair); + assertEquals(10, s.availablePermits()); + s.reducePermits(0); + assertEquals(10, s.availablePermits()); + s.reducePermits(1); + assertEquals(9, s.availablePermits()); + s.reducePermits(10); + assertEquals(-1, s.availablePermits()); + s.reducePermits(10); + assertEquals(-11, s.availablePermits()); + s.reducePermits(0); + assertEquals(-11, s.availablePermits()); + } + + /** + * a reserialized semaphore has same number of permits and + * fairness, but no queued threads */ - public void testAcquireReleaseInDifferentThreads() { - final Semaphore s = new Semaphore(1); - Thread t = new Thread(new Runnable() { - public void run() { - try { - s.acquire(); - s.release(); - s.release(); - s.acquire(); - } catch(InterruptedException ie){ - threadUnexpectedException(); - } - } - }); - t.start(); + public void testSerialization() { testSerialization(false); } + public void testSerialization_fair() { testSerialization(true); } + public void testSerialization(boolean fair) { try { - s.release(); - s.release(); + Semaphore s = new Semaphore(3, fair); s.acquire(); s.acquire(); - t.join(); - } catch( InterruptedException e){ - unexpectedException(); + s.release(); + + Semaphore clone = serialClone(s); + assertEquals(fair, s.isFair()); + assertEquals(fair, clone.isFair()); + assertEquals(2, s.availablePermits()); + assertEquals(2, clone.availablePermits()); + clone.acquire(); + clone.acquire(); + clone.release(); + assertEquals(2, s.availablePermits()); + assertEquals(1, clone.availablePermits()); + assertFalse(s.hasQueuedThreads()); + assertFalse(clone.hasQueuedThreads()); + } catch (InterruptedException e) { threadUnexpectedException(e); } + + { + PublicSemaphore s = new PublicSemaphore(0, fair); + Thread t = newStartedThread(new InterruptibleLockRunnable(s)); + // waitForQueuedThreads(s); // suffers from "flicker", so ... + waitForQueuedThread(s, t); // ... we use this instead + PublicSemaphore clone = serialClone(s); + assertEquals(fair, s.isFair()); + assertEquals(fair, clone.isFair()); + assertEquals(0, s.availablePermits()); + assertEquals(0, clone.availablePermits()); + assertTrue(s.hasQueuedThreads()); + assertFalse(clone.hasQueuedThreads()); + s.release(); + awaitTermination(t); + assertFalse(s.hasQueuedThreads()); + assertFalse(clone.hasQueuedThreads()); } } /** - * + * tryAcquire(n) succeeds when sufficient permits, else fails */ - public void testTimedAcquireReleaseInSameThread() { - Semaphore s = new Semaphore(1); - try { - assertTrue(s.tryAcquire(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); - s.release(); - assertTrue(s.tryAcquire(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); - s.release(); - assertTrue(s.tryAcquire(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); - s.release(); - assertTrue(s.tryAcquire(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); - s.release(); - assertTrue(s.tryAcquire(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); - s.release(); - assertEquals(1, s.availablePermits()); - } catch( InterruptedException e){ - unexpectedException(); - } + public void testTryAcquireNInSameThread() { testTryAcquireNInSameThread(false); } + public void testTryAcquireNInSameThread_fair() { testTryAcquireNInSameThread(true); } + public void testTryAcquireNInSameThread(boolean fair) { + Semaphore s = new Semaphore(2, fair); + assertEquals(2, s.availablePermits()); + assertFalse(s.tryAcquire(3)); + assertEquals(2, s.availablePermits()); + assertTrue(s.tryAcquire(2)); + assertEquals(0, s.availablePermits()); + assertFalse(s.tryAcquire(1)); + assertFalse(s.tryAcquire(2)); + assertEquals(0, s.availablePermits()); } /** - * + * acquire succeeds if permits available */ - public void testTimedAcquireReleaseInDifferentThreads() { - final Semaphore s = new Semaphore(1); - Thread t = new Thread(new Runnable() { - public void run() { - try { - s.release(); - threadAssertTrue(s.tryAcquire(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); - s.release(); - threadAssertTrue(s.tryAcquire(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); - - } catch(InterruptedException ie){ - threadUnexpectedException(); - } - } - }); - t.start(); - try { - assertTrue(s.tryAcquire(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); - s.release(); - assertTrue(s.tryAcquire(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); - s.release(); - t.join(); - } catch( InterruptedException e){ - unexpectedException(); + public void testReleaseAcquireSameThread_acquire() { testReleaseAcquireSameThread(false, AcquireMethod.acquire); } + public void testReleaseAcquireSameThread_acquire_fair() { testReleaseAcquireSameThread(true, AcquireMethod.acquire); } + public void testReleaseAcquireSameThread_acquireN() { testReleaseAcquireSameThread(false, AcquireMethod.acquireN); } + public void testReleaseAcquireSameThread_acquireN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.acquireN); } + public void testReleaseAcquireSameThread_acquireUninterruptibly() { testReleaseAcquireSameThread(false, AcquireMethod.acquireUninterruptibly); } + public void testReleaseAcquireSameThread_acquireUninterruptibly_fair() { testReleaseAcquireSameThread(true, AcquireMethod.acquireUninterruptibly); } + public void testReleaseAcquireSameThread_acquireUninterruptiblyN() { testReleaseAcquireSameThread(false, AcquireMethod.acquireUninterruptibly); } + public void testReleaseAcquireSameThread_acquireUninterruptiblyN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.acquireUninterruptibly); } + public void testReleaseAcquireSameThread_tryAcquire() { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquire); } + public void testReleaseAcquireSameThread_tryAcquire_fair() { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquire); } + public void testReleaseAcquireSameThread_tryAcquireN() { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquireN); } + public void testReleaseAcquireSameThread_tryAcquireN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquireN); } + public void testReleaseAcquireSameThread_tryAcquireTimed() { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquireTimed); } + public void testReleaseAcquireSameThread_tryAcquireTimed_fair() { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquireTimed); } + public void testReleaseAcquireSameThread_tryAcquireTimedN() { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquireTimedN); } + public void testReleaseAcquireSameThread_tryAcquireTimedN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquireTimedN); } + public void testReleaseAcquireSameThread(boolean fair, + final AcquireMethod acquirer) { + Semaphore s = new Semaphore(1, fair); + for (int i = 1; i < 6; i++) { + s.release(i); + assertEquals(1 + i, s.availablePermits()); + try { + acquirer.acquire(s, i); + } catch (InterruptedException e) { threadUnexpectedException(e); } + assertEquals(1, s.availablePermits()); } } /** - * - */ - public void testAcquire_InterruptedException() { - final Semaphore s = new Semaphore(0); - Thread t = new Thread(new Runnable() { - public void run() { - try { - s.acquire(); - threadShouldThrow(); - } catch(InterruptedException success){} - } - }); - t.start(); - try { - Thread.sleep(SHORT_DELAY_MS); - t.interrupt(); - t.join(); - } catch(InterruptedException e){ - unexpectedException(); - } - } - - /** - * - */ - public void testTryAcquire_InterruptedException() { - final Semaphore s = new Semaphore(0); - Thread t = new Thread(new Runnable() { - public void run() { - try { - s.tryAcquire(MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS); - threadShouldThrow(); - } catch(InterruptedException success){ - } - } - }); - t.start(); - try { - Thread.sleep(SHORT_DELAY_MS); - t.interrupt(); - t.join(); - } catch(InterruptedException e){ - unexpectedException(); + * release in one thread enables acquire in another thread + */ + public void testReleaseAcquireDifferentThreads_acquire() { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquire); } + public void testReleaseAcquireDifferentThreads_acquire_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquire); } + public void testReleaseAcquireDifferentThreads_acquireN() { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquireN); } + public void testReleaseAcquireDifferentThreads_acquireN_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquireN); } + public void testReleaseAcquireDifferentThreads_acquireUninterruptibly() { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquireUninterruptibly); } + public void testReleaseAcquireDifferentThreads_acquireUninterruptibly_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquireUninterruptibly); } + public void testReleaseAcquireDifferentThreads_acquireUninterruptiblyN() { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquireUninterruptibly); } + public void testReleaseAcquireDifferentThreads_acquireUninterruptiblyN_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquireUninterruptibly); } + public void testReleaseAcquireDifferentThreads_tryAcquireTimed() { testReleaseAcquireDifferentThreads(false, AcquireMethod.tryAcquireTimed); } + public void testReleaseAcquireDifferentThreads_tryAcquireTimed_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.tryAcquireTimed); } + public void testReleaseAcquireDifferentThreads_tryAcquireTimedN() { testReleaseAcquireDifferentThreads(false, AcquireMethod.tryAcquireTimedN); } + public void testReleaseAcquireDifferentThreads_tryAcquireTimedN_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.tryAcquireTimedN); } + public void testReleaseAcquireDifferentThreads(boolean fair, + final AcquireMethod acquirer) { + final Semaphore s = new Semaphore(0, fair); + final int rounds = 4; + long startTime = System.nanoTime(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < rounds; i++) { + assertFalse(s.hasQueuedThreads()); + if (i % 2 == 0) + acquirer.acquire(s); + else + acquirer.acquire(s, 3); + }}}); + + for (int i = 0; i < rounds; i++) { + while (! (s.availablePermits() == 0 && s.hasQueuedThreads())) + Thread.yield(); + assertTrue(t.isAlive()); + if (i % 2 == 0) + s.release(); + else + s.release(3); } + awaitTermination(t); + assertEquals(0, s.availablePermits()); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); } /** - * + * fair locks are strictly FIFO */ - public void testSerialization() { - Semaphore l = new Semaphore(3); - try { - l.acquire(); - l.release(); - ByteArrayOutputStream bout = new ByteArrayOutputStream(10000); - ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(bout)); - out.writeObject(l); - out.close(); - - ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); - ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(bin)); - Semaphore r = (Semaphore) in.readObject(); - assertEquals(3, r.availablePermits()); - r.acquire(); - r.release(); - } catch(Exception e){ - unexpectedException(); - } + public void testFairLocksFifo() { + final PublicSemaphore s = new PublicSemaphore(1, true); + final CountDownLatch pleaseRelease = new CountDownLatch(1); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + // Will block; permits are available, but not three + s.acquire(3); + }}); + + waitForQueuedThread(s, t1); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + // Will fail, even though 1 permit is available + assertFalse( + s.tryAcquire(randomExpiredTimeout(), randomTimeUnit())); + assertFalse( + s.tryAcquire(1, randomExpiredTimeout(), randomTimeUnit())); + + // untimed tryAcquire will barge and succeed + assertTrue(s.tryAcquire()); + s.release(2); + assertTrue(s.tryAcquire(2)); + s.release(); + + pleaseRelease.countDown(); + // Will queue up behind t1, even though 1 permit is available + s.acquire(); + }}); + + await(pleaseRelease); + waitForQueuedThread(s, t2); + s.release(2); + awaitTermination(t1); + assertTrue(t2.isAlive()); + s.release(); + awaitTermination(t2); + } + + /** + * toString indicates current number of permits + */ + public void testToString() { testToString(false); } + public void testToString_fair() { testToString(true); } + public void testToString(boolean fair) { + PublicSemaphore s = new PublicSemaphore(0, fair); + assertTrue(s.toString().contains("Permits = 0")); + s.release(); + assertTrue(s.toString().contains("Permits = 1")); + s.release(2); + assertTrue(s.toString().contains("Permits = 3")); + s.reducePermits(5); + assertTrue(s.toString().contains("Permits = -2")); } }