--- jsr166/src/test/tck/LockSupportTest.java 2010/10/04 20:15:16 1.15 +++ jsr166/src/test/tck/LockSupportTest.java 2017/05/12 18:23:11 1.28 @@ -2,347 +2,376 @@ * Written by Doug Lea and Martin Buchholz with assistance from * members of JCP JSR-166 Expert Group and released to the public * domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * 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 static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.*; +import java.util.concurrent.locks.LockSupport; + +import junit.framework.Test; +import junit.framework.TestSuite; public class LockSupportTest extends JSR166TestCase { public static void main(String[] args) { - junit.textui.TestRunner.run(suite()); + main(suite(), args); } public static Test suite() { return new TestSuite(LockSupportTest.class); } - /** - * park is released by subsequent unpark - */ - public void testParkBeforeUnpark() throws InterruptedException { - final CountDownLatch threadStarted = new CountDownLatch(1); - Thread t = new Thread(new CheckedRunnable() { - public void realRun() { - threadStarted.countDown(); - LockSupport.park(); - }}); - - t.start(); - threadStarted.await(); - Thread.sleep(SHORT_DELAY_MS); - LockSupport.unpark(t); - joinWith(t); + static { + // Reduce the risk of rare disastrous classloading in first call to + // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773 + Class ensureLoaded = LockSupport.class; } /** - * parkUntil is released by subsequent unpark + * Returns the blocker object used by tests in this file. + * Any old object will do; we'll return a convenient one. */ - public void testParkUntilBeforeUnpark() throws InterruptedException { - final CountDownLatch threadStarted = new CountDownLatch(1); - Thread t = new Thread(new CheckedRunnable() { - public void realRun() { - long d = new Date().getTime() + LONG_DELAY_MS; - long nanos = LONG_DELAY_MS * 1000L * 1000L; - long t0 = System.nanoTime(); - threadStarted.countDown(); - LockSupport.parkUntil(d); - assertTrue(System.nanoTime() - t0 < nanos); - }}); + static Object theBlocker() { + return LockSupportTest.class; + } - t.start(); - threadStarted.await(); - Thread.sleep(SHORT_DELAY_MS); - LockSupport.unpark(t); - joinWith(t); + enum ParkMethod { + park() { + void park() { + LockSupport.park(); + } + void park(long millis) { + throw new UnsupportedOperationException(); + } + }, + parkUntil() { + void park(long millis) { + LockSupport.parkUntil(deadline(millis)); + } + }, + parkNanos() { + void park(long millis) { + LockSupport.parkNanos(MILLISECONDS.toNanos(millis)); + } + }, + parkBlocker() { + void park() { + LockSupport.park(theBlocker()); + } + void park(long millis) { + throw new UnsupportedOperationException(); + } + }, + parkUntilBlocker() { + void park(long millis) { + LockSupport.parkUntil(theBlocker(), deadline(millis)); + } + }, + parkNanosBlocker() { + void park(long millis) { + LockSupport.parkNanos(theBlocker(), + MILLISECONDS.toNanos(millis)); + } + }; + + void park() { park(2 * LONG_DELAY_MS); } + abstract void park(long millis); + + /** Returns a deadline to use with parkUntil. */ + long deadline(long millis) { + // beware of rounding + return System.currentTimeMillis() + millis + 1; + } } /** - * parkNanos is released by subsequent unpark + * park is released by subsequent unpark */ - public void testParkNanosBeforeUnpark() throws InterruptedException { - final CountDownLatch threadStarted = new CountDownLatch(1); - Thread t = new Thread(new CheckedRunnable() { + public void testParkBeforeUnpark_park() { + testParkBeforeUnpark(ParkMethod.park); + } + public void testParkBeforeUnpark_parkNanos() { + testParkBeforeUnpark(ParkMethod.parkNanos); + } + public void testParkBeforeUnpark_parkUntil() { + testParkBeforeUnpark(ParkMethod.parkUntil); + } + public void testParkBeforeUnpark_parkBlocker() { + testParkBeforeUnpark(ParkMethod.parkBlocker); + } + public void testParkBeforeUnpark_parkNanosBlocker() { + testParkBeforeUnpark(ParkMethod.parkNanosBlocker); + } + public void testParkBeforeUnpark_parkUntilBlocker() { + testParkBeforeUnpark(ParkMethod.parkUntilBlocker); + } + public void testParkBeforeUnpark(final ParkMethod parkMethod) { + final CountDownLatch pleaseUnpark = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { public void realRun() { - long nanos = LONG_DELAY_MS * 1000L * 1000L; - long t0 = System.nanoTime(); - threadStarted.countDown(); - LockSupport.parkNanos(nanos); - assertTrue(System.nanoTime() - t0 < nanos); + pleaseUnpark.countDown(); + parkMethod.park(); }}); - t.start(); - threadStarted.await(); - Thread.sleep(SHORT_DELAY_MS); + await(pleaseUnpark); LockSupport.unpark(t); - joinWith(t); + awaitTermination(t); } /** * park is released by preceding unpark */ - public void testParkAfterUnpark() throws Exception { - final CountDownLatch threadStarted = new CountDownLatch(1); - final AtomicBoolean unparked = new AtomicBoolean(false); - Thread t = new Thread(new CheckedRunnable() { - public void realRun() throws Exception { - threadStarted.countDown(); - while (!unparked.get()) - Thread.yield(); - LockSupport.park(); - }}); - - t.start(); - threadStarted.await(); - LockSupport.unpark(t); - unparked.set(true); - joinWith(t); + public void testParkAfterUnpark_park() { + testParkAfterUnpark(ParkMethod.park); } - - /** - * parkUntil is released by preceding unpark - */ - public void testParkUntilAfterUnpark() throws Exception { - final CountDownLatch threadStarted = new CountDownLatch(1); - final AtomicBoolean unparked = new AtomicBoolean(false); - Thread t = new Thread(new CheckedRunnable() { - public void realRun() throws Exception { - threadStarted.countDown(); - while (!unparked.get()) - Thread.yield(); - long d = new Date().getTime() + LONG_DELAY_MS; - long nanos = LONG_DELAY_MS * 1000L * 1000L; - long t0 = System.nanoTime(); - LockSupport.parkUntil(d); - assertTrue(System.nanoTime() - t0 < nanos); - }}); - - t.start(); - threadStarted.await(); - LockSupport.unpark(t); - unparked.set(true); - joinWith(t); + public void testParkAfterUnpark_parkNanos() { + testParkAfterUnpark(ParkMethod.parkNanos); } - - /** - * parkNanos is released by preceding unpark - */ - public void testParkNanosAfterUnpark() throws Exception { - final CountDownLatch threadStarted = new CountDownLatch(1); - final AtomicBoolean unparked = new AtomicBoolean(false); - Thread t = new Thread(new CheckedRunnable() { - public void realRun() throws Exception { - threadStarted.countDown(); - while (!unparked.get()) + public void testParkAfterUnpark_parkUntil() { + testParkAfterUnpark(ParkMethod.parkUntil); + } + public void testParkAfterUnpark_parkBlocker() { + testParkAfterUnpark(ParkMethod.parkBlocker); + } + public void testParkAfterUnpark_parkNanosBlocker() { + testParkAfterUnpark(ParkMethod.parkNanosBlocker); + } + public void testParkAfterUnpark_parkUntilBlocker() { + testParkAfterUnpark(ParkMethod.parkUntilBlocker); + } + public void testParkAfterUnpark(final ParkMethod parkMethod) { + final CountDownLatch pleaseUnpark = new CountDownLatch(1); + final AtomicBoolean pleasePark = new AtomicBoolean(false); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + pleaseUnpark.countDown(); + while (!pleasePark.get()) Thread.yield(); - long nanos = LONG_DELAY_MS * 1000L * 1000L; - long t0 = System.nanoTime(); - LockSupport.parkNanos(nanos); - assertTrue(System.nanoTime() - t0 < nanos); + parkMethod.park(); }}); - t.start(); - threadStarted.await(); + await(pleaseUnpark); LockSupport.unpark(t); - unparked.set(true); - joinWith(t); + pleasePark.set(true); + awaitTermination(t); } /** * park is released by subsequent interrupt */ - public void testParkBeforeInterrupt() throws InterruptedException { - final CountDownLatch threadStarted = new CountDownLatch(1); - Thread t = new Thread(new CheckedRunnable() { - public void realRun() { - assertFalse(Thread.currentThread().isInterrupted()); - threadStarted.countDown(); - LockSupport.park(); - assertTrue(Thread.currentThread().isInterrupted()); - }}); - - t.start(); - threadStarted.await(); - Thread.sleep(SHORT_DELAY_MS); - t.interrupt(); - joinWith(t); + public void testParkBeforeInterrupt_park() { + testParkBeforeInterrupt(ParkMethod.park); } - - /** - * parkUntil is released by subsequent interrupt - */ - public void testParkUntilBeforeInterrupt() throws InterruptedException { - final CountDownLatch threadStarted = new CountDownLatch(1); - Thread t = new Thread(new CheckedRunnable() { - public void realRun() { - long d = new Date().getTime() + LONG_DELAY_MS; - long nanos = LONG_DELAY_MS * 1000L * 1000L; - long t0 = System.nanoTime(); - threadStarted.countDown(); - LockSupport.parkUntil(d); - assertTrue(System.nanoTime() - t0 < nanos); - }}); - - t.start(); - threadStarted.await(); - Thread.sleep(SHORT_DELAY_MS); - t.interrupt(); - joinWith(t); + public void testParkBeforeInterrupt_parkNanos() { + testParkBeforeInterrupt(ParkMethod.parkNanos); } - - /** - * parkNanos is released by subsequent interrupt - */ - public void testParkNanosBeforeInterrupt() throws InterruptedException { - final CountDownLatch threadStarted = new CountDownLatch(1); - Thread t = new Thread(new CheckedRunnable() { + public void testParkBeforeInterrupt_parkUntil() { + testParkBeforeInterrupt(ParkMethod.parkUntil); + } + public void testParkBeforeInterrupt_parkBlocker() { + testParkBeforeInterrupt(ParkMethod.parkBlocker); + } + public void testParkBeforeInterrupt_parkNanosBlocker() { + testParkBeforeInterrupt(ParkMethod.parkNanosBlocker); + } + public void testParkBeforeInterrupt_parkUntilBlocker() { + testParkBeforeInterrupt(ParkMethod.parkUntilBlocker); + } + public void testParkBeforeInterrupt(final ParkMethod parkMethod) { + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { public void realRun() { - long nanos = LONG_DELAY_MS * 1000L * 1000L; - long t0 = System.nanoTime(); - threadStarted.countDown(); - LockSupport.parkNanos(nanos); - assertTrue(System.nanoTime() - t0 < nanos); + pleaseInterrupt.countDown(); + do { + parkMethod.park(); + // park may return spuriously + } while (! Thread.interrupted()); }}); - t.start(); - threadStarted.await(); - Thread.sleep(SHORT_DELAY_MS); + await(pleaseInterrupt); + assertThreadStaysAlive(t); t.interrupt(); - joinWith(t); + awaitTermination(t); } /** * park is released by preceding interrupt */ - public void testParkAfterInterrupt() throws Exception { - final CountDownLatch threadStarted = new CountDownLatch(1); - final AtomicBoolean unparked = new AtomicBoolean(false); - Thread t = new Thread(new CheckedRunnable() { + public void testParkAfterInterrupt_park() { + testParkAfterInterrupt(ParkMethod.park); + } + public void testParkAfterInterrupt_parkNanos() { + testParkAfterInterrupt(ParkMethod.parkNanos); + } + public void testParkAfterInterrupt_parkUntil() { + testParkAfterInterrupt(ParkMethod.parkUntil); + } + public void testParkAfterInterrupt_parkBlocker() { + testParkAfterInterrupt(ParkMethod.parkBlocker); + } + public void testParkAfterInterrupt_parkNanosBlocker() { + testParkAfterInterrupt(ParkMethod.parkNanosBlocker); + } + public void testParkAfterInterrupt_parkUntilBlocker() { + testParkAfterInterrupt(ParkMethod.parkUntilBlocker); + } + public void testParkAfterInterrupt(final ParkMethod parkMethod) { + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + final AtomicBoolean pleasePark = new AtomicBoolean(false); + Thread t = newStartedThread(new CheckedRunnable() { public void realRun() throws Exception { - threadStarted.countDown(); - while (!unparked.get()) + pleaseInterrupt.countDown(); + while (!pleasePark.get()) Thread.yield(); assertTrue(Thread.currentThread().isInterrupted()); - LockSupport.park(); + parkMethod.park(); assertTrue(Thread.currentThread().isInterrupted()); }}); - t.start(); - threadStarted.await(); + await(pleaseInterrupt); t.interrupt(); - unparked.set(true); - joinWith(t); + pleasePark.set(true); + awaitTermination(t); } /** - * parkUntil is released by preceding interrupt + * timed park times out if not unparked */ - public void testParkUntilAfterInterrupt() throws Exception { - final CountDownLatch threadStarted = new CountDownLatch(1); - final AtomicBoolean unparked = new AtomicBoolean(false); - Thread t = new Thread(new CheckedRunnable() { - public void realRun() throws Exception { - threadStarted.countDown(); - while (!unparked.get()) - Thread.yield(); - long d = new Date().getTime() + LONG_DELAY_MS; - long nanos = LONG_DELAY_MS * 1000L * 1000L; - long t0 = System.nanoTime(); - LockSupport.parkUntil(d); - assertTrue(System.nanoTime() - t0 < nanos); + public void testParkTimesOut_parkNanos() { + testParkTimesOut(ParkMethod.parkNanos); + } + public void testParkTimesOut_parkUntil() { + testParkTimesOut(ParkMethod.parkUntil); + } + public void testParkTimesOut_parkNanosBlocker() { + testParkTimesOut(ParkMethod.parkNanosBlocker); + } + public void testParkTimesOut_parkUntilBlocker() { + testParkTimesOut(ParkMethod.parkUntilBlocker); + } + public void testParkTimesOut(final ParkMethod parkMethod) { + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + for (int tries = MAX_SPURIOUS_WAKEUPS; tries-->0; ) { + long startTime = System.nanoTime(); + parkMethod.park(timeoutMillis()); + // park may return spuriously + if (millisElapsedSince(startTime) >= timeoutMillis()) + return; + } + fail("too many consecutive spurious wakeups?"); }}); - t.start(); - threadStarted.await(); - t.interrupt(); - unparked.set(true); - joinWith(t); + awaitTermination(t); } /** - * parkNanos is released by preceding interrupt + * getBlocker(null) throws NullPointerException */ - public void testParkNanosAfterInterrupt() throws Exception { - final CountDownLatch threadStarted = new CountDownLatch(1); - final AtomicBoolean unparked = new AtomicBoolean(false); - Thread t = new Thread(new CheckedRunnable() { - public void realRun() throws Exception { - threadStarted.countDown(); - while (!unparked.get()) - Thread.yield(); - long nanos = LONG_DELAY_MS * 1000L * 1000L; - long t0 = System.nanoTime(); - LockSupport.parkNanos(nanos); - assertTrue(System.nanoTime() - t0 < nanos); - }}); - - t.start(); - threadStarted.await(); - t.interrupt(); - unparked.set(true); - joinWith(t); + public void testGetBlockerNull() { + try { + LockSupport.getBlocker(null); + shouldThrow(); + } catch (NullPointerException success) {} } /** - * parkNanos times out if not unparked + * getBlocker returns the blocker object passed to park */ - public void testParkNanosTimesOut() throws InterruptedException { - Thread t = new Thread(new CheckedRunnable() { + public void testGetBlocker_parkBlocker() { + testGetBlocker(ParkMethod.parkBlocker); + } + public void testGetBlocker_parkNanosBlocker() { + testGetBlocker(ParkMethod.parkNanosBlocker); + } + public void testGetBlocker_parkUntilBlocker() { + testGetBlocker(ParkMethod.parkUntilBlocker); + } + public void testGetBlocker(final ParkMethod parkMethod) { + final CountDownLatch started = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { public void realRun() { - final long timeoutNanos = SHORT_DELAY_MS * 1000L * 1000L; - long t0 = System.nanoTime(); - LockSupport.parkNanos(timeoutNanos); - assertTrue(System.nanoTime() - t0 >= timeoutNanos); - }}); - - t.start(); - joinWith(t); + Thread t = Thread.currentThread(); + started.countDown(); + do { + assertNull(LockSupport.getBlocker(t)); + parkMethod.park(); + assertNull(LockSupport.getBlocker(t)); + // park may return spuriously + } while (! Thread.interrupted()); + }}); + + long startTime = System.nanoTime(); + await(started); + for (;;) { + Object x = LockSupport.getBlocker(t); + if (x == theBlocker()) { // success + t.interrupt(); + awaitTermination(t); + assertNull(LockSupport.getBlocker(t)); + return; + } else { + assertNull(x); // ok + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + fail("timed out"); + Thread.yield(); + } + } } - /** - * parkUntil times out if not unparked + * timed park(0) returns immediately. + * + * Requires hotspot fix for: + * 6763959 java.util.concurrent.locks.LockSupport.parkUntil(0) blocks forever + * which is in jdk7-b118 and 6u25. */ - public void testParkUntilTimesOut() throws InterruptedException { - Thread t = new Thread(new CheckedRunnable() { + public void testPark0_parkNanos() { + testPark0(ParkMethod.parkNanos); + } + public void testPark0_parkUntil() { + testPark0(ParkMethod.parkUntil); + } + public void testPark0_parkNanosBlocker() { + testPark0(ParkMethod.parkNanosBlocker); + } + public void testPark0_parkUntilBlocker() { + testPark0(ParkMethod.parkUntilBlocker); + } + public void testPark0(final ParkMethod parkMethod) { + Thread t = newStartedThread(new CheckedRunnable() { public void realRun() { - long d = new Date().getTime() + SHORT_DELAY_MS; - // beware of rounding - long timeoutNanos = (SHORT_DELAY_MS - 1) * 1000L * 1000L; - long t0 = System.nanoTime(); - LockSupport.parkUntil(d); - assertTrue(System.nanoTime() - t0 >= timeoutNanos); + parkMethod.park(0L); }}); - t.start(); - joinWith(t); + awaitTermination(t); } /** - * parkUntil(0) returns immediately - * Requires hotspot fix for: - * 6763959 java.util.concurrent.locks.LockSupport.parkUntil(0) blocks forever + * timed park(Long.MIN_VALUE) returns immediately. */ - public void XXXtestParkUntil0Returns() throws InterruptedException { - Thread t = new Thread(new CheckedRunnable() { + public void testParkNeg_parkNanos() { + testParkNeg(ParkMethod.parkNanos); + } + public void testParkNeg_parkUntil() { + testParkNeg(ParkMethod.parkUntil); + } + public void testParkNeg_parkNanosBlocker() { + testParkNeg(ParkMethod.parkNanosBlocker); + } + public void testParkNeg_parkUntilBlocker() { + testParkNeg(ParkMethod.parkUntilBlocker); + } + public void testParkNeg(final ParkMethod parkMethod) { + Thread t = newStartedThread(new CheckedRunnable() { public void realRun() { - LockSupport.parkUntil(0L); + parkMethod.park(Long.MIN_VALUE); }}); - t.start(); - joinWith(t); - } - - private void joinWith(Thread t) throws InterruptedException { - t.join(MEDIUM_DELAY_MS); - if (t.isAlive()) { - fail("Test timed out"); - t.interrupt(); - } + awaitTermination(t); } }