|  | /* | 
|  | * 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/publicdomain/zero/1.0/ | 
|  | * Other contributors include Andrew Wright, Jeffrey Hayes, | 
|  | * Pat Fisher, Mike Judd. | 
|  | */ | 
|  |  | 
|  | package jsr166; | 
|  |  | 
|  | import static java.util.concurrent.TimeUnit.MILLISECONDS; | 
|  |  | 
|  | import java.util.concurrent.CountDownLatch; | 
|  | import java.util.concurrent.atomic.AtomicBoolean; | 
|  | import java.util.concurrent.locks.LockSupport; | 
|  |  | 
|  | import junit.framework.Test; | 
|  | import junit.framework.TestSuite; | 
|  |  | 
|  | public class LockSupportTest extends JSR166TestCase { | 
|  | // android-note: Removed because the CTS runner does a bad job of | 
|  | // retrying tests that have suite() declarations. | 
|  | // | 
|  | // public static void main(String[] args) { | 
|  | //     main(suite(), args); | 
|  | // } | 
|  | // public static Test suite() { | 
|  | //     return new TestSuite(LockSupportTest.class); | 
|  | // } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns the blocker object used by tests in this file. | 
|  | * Any old object will do; we'll return a convenient one. | 
|  | */ | 
|  | static Object theBlocker() { | 
|  | return LockSupportTest.class; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * park is released by subsequent unpark | 
|  | */ | 
|  | 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() { | 
|  | pleaseUnpark.countDown(); | 
|  | parkMethod.park(); | 
|  | }}); | 
|  |  | 
|  | await(pleaseUnpark); | 
|  | LockSupport.unpark(t); | 
|  | awaitTermination(t); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * park is released by preceding unpark | 
|  | */ | 
|  | public void testParkAfterUnpark_park() { | 
|  | testParkAfterUnpark(ParkMethod.park); | 
|  | } | 
|  | public void testParkAfterUnpark_parkNanos() { | 
|  | testParkAfterUnpark(ParkMethod.parkNanos); | 
|  | } | 
|  | 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(); | 
|  | parkMethod.park(); | 
|  | }}); | 
|  |  | 
|  | await(pleaseUnpark); | 
|  | LockSupport.unpark(t); | 
|  | pleasePark.set(true); | 
|  | awaitTermination(t); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * park is released by subsequent interrupt | 
|  | */ | 
|  | public void testParkBeforeInterrupt_park() { | 
|  | testParkBeforeInterrupt(ParkMethod.park); | 
|  | } | 
|  | public void testParkBeforeInterrupt_parkNanos() { | 
|  | testParkBeforeInterrupt(ParkMethod.parkNanos); | 
|  | } | 
|  | 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() { | 
|  | pleaseInterrupt.countDown(); | 
|  | do { | 
|  | parkMethod.park(); | 
|  | // park may return spuriously | 
|  | } while (! Thread.currentThread().isInterrupted()); | 
|  | }}); | 
|  |  | 
|  | await(pleaseInterrupt); | 
|  | assertThreadStaysAlive(t); | 
|  | t.interrupt(); | 
|  | awaitTermination(t); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * park is released by preceding interrupt | 
|  | */ | 
|  | 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 { | 
|  | pleaseInterrupt.countDown(); | 
|  | while (!pleasePark.get()) | 
|  | Thread.yield(); | 
|  | assertTrue(Thread.currentThread().isInterrupted()); | 
|  | parkMethod.park(); | 
|  | assertTrue(Thread.currentThread().isInterrupted()); | 
|  | }}); | 
|  |  | 
|  | await(pleaseInterrupt); | 
|  | t.interrupt(); | 
|  | pleasePark.set(true); | 
|  | awaitTermination(t); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * timed park times out if not unparked | 
|  | */ | 
|  | 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 (;;) { | 
|  | long startTime = System.nanoTime(); | 
|  | parkMethod.park(timeoutMillis()); | 
|  | // park may return spuriously | 
|  | if (millisElapsedSince(startTime) >= timeoutMillis()) | 
|  | return; | 
|  | } | 
|  | }}); | 
|  |  | 
|  | awaitTermination(t); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * getBlocker(null) throws NullPointerException | 
|  | */ | 
|  | public void testGetBlockerNull() { | 
|  | try { | 
|  | LockSupport.getBlocker(null); | 
|  | shouldThrow(); | 
|  | } catch (NullPointerException success) {} | 
|  | } | 
|  |  | 
|  | /** | 
|  | * getBlocker returns the blocker object passed to park | 
|  | */ | 
|  | 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() { | 
|  | Thread t = Thread.currentThread(); | 
|  | started.countDown(); | 
|  | do { | 
|  | assertNull(LockSupport.getBlocker(t)); | 
|  | parkMethod.park(); | 
|  | assertNull(LockSupport.getBlocker(t)); | 
|  | // park may return spuriously | 
|  | } while (! Thread.currentThread().isInterrupted()); | 
|  | }}); | 
|  |  | 
|  | 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(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * 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 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() { | 
|  | parkMethod.park(0L); | 
|  | }}); | 
|  |  | 
|  | awaitTermination(t); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * timed park(Long.MIN_VALUE) returns immediately. | 
|  | */ | 
|  | 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() { | 
|  | parkMethod.park(Long.MIN_VALUE); | 
|  | }}); | 
|  |  | 
|  | awaitTermination(t); | 
|  | } | 
|  | } |