diff options
Diffstat (limited to 'harmony-tests/src/test/java/org/apache/harmony/tests/org/apache/harmony/kernel/dalvik/ThreadsTest.java')
-rw-r--r-- | harmony-tests/src/test/java/org/apache/harmony/tests/org/apache/harmony/kernel/dalvik/ThreadsTest.java | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/org/apache/harmony/kernel/dalvik/ThreadsTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/org/apache/harmony/kernel/dalvik/ThreadsTest.java new file mode 100644 index 0000000..19c6229 --- /dev/null +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/org/apache/harmony/kernel/dalvik/ThreadsTest.java @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.api.org.apache.harmony.kernel.dalvik; + +import java.lang.reflect.Field; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.TimeUnit; +import junit.framework.Assert; +import junit.framework.TestCase; +import sun.misc.Unsafe; + +/** + * Tests for the <code>park()</code> functionality of {@link Unsafe}. + */ +public class ThreadsTest extends TestCase { + private static Unsafe UNSAFE = null; + static { + /* + * Set up {@link #UNSAFE}. This subverts the access check to + * get the unique Unsafe instance. We can do this because + * there's no security manager installed when running the + * test. + */ + try { + Field field = Unsafe.class.getDeclaredField("THE_ONE"); + field.setAccessible(true); + + UNSAFE = (Unsafe) field.get(null); + } catch (NoSuchFieldException ex) { + throw new RuntimeException(ex); + } catch (IllegalAccessException ex) { + throw new RuntimeException(ex); + } + } + + /** Test the case where the park times out. */ + public void test_parkFor_1() throws Exception { + CyclicBarrier barrier = new CyclicBarrier(2); + Parker parker = new Parker(barrier, false, 500); + Thread parkerThread = new Thread(parker); + Thread waiterThread = + new Thread(new WaitAndUnpark(barrier, 1000, parkerThread)); + + parkerThread.start(); + waiterThread.start(); + parker.assertDurationIsInRange(500); + waiterThread.join(); + parkerThread.join(); + } + + /** Test the case where the unpark happens before the timeout. */ + public void test_parkFor_2() throws Exception { + CyclicBarrier barrier = new CyclicBarrier(2); + Parker parker = new Parker(barrier, false, 1000); + Thread parkerThread = new Thread(parker); + Thread waiterThread = + new Thread(new WaitAndUnpark(barrier, 300, parkerThread)); + + parkerThread.start(); + waiterThread.start(); + parker.assertDurationIsInRange(300); + waiterThread.join(); + parkerThread.join(); + } + + /** Test the case where the thread is preemptively unparked. */ + public void test_parkFor_3() throws Exception { + CyclicBarrier barrier = new CyclicBarrier(1); + Parker parker = new Parker(barrier, false, 1000); + Thread parkerThread = new Thread(parker); + + UNSAFE.unpark(parkerThread); + parkerThread.start(); + parker.assertDurationIsInRange(0); + parkerThread.join(); + } + + /** Test the case where the park times out. */ + public void test_parkUntil_1() throws Exception { + CyclicBarrier barrier = new CyclicBarrier(2); + Parker parker = new Parker(barrier, true, 500); + Thread parkerThread = new Thread(parker); + Thread waiterThread = + new Thread(new WaitAndUnpark(barrier, 1000, parkerThread)); + + parkerThread.start(); + waiterThread.start(); + parker.assertDurationIsInRange(500); + waiterThread.join(); + parkerThread.join(); + } + + /** Test the case where the unpark happens before the timeout. */ + public void test_parkUntil_2() throws Exception { + CyclicBarrier barrier = new CyclicBarrier(2); + Parker parker = new Parker(barrier, true, 1000); + Thread parkerThread = new Thread(parker); + Thread waiterThread = + new Thread(new WaitAndUnpark(barrier, 300, parkerThread)); + + parkerThread.start(); + waiterThread.start(); + parker.assertDurationIsInRange(300); + waiterThread.join(); + parkerThread.join(); + } + + /** Test the case where the thread is preemptively unparked. */ + public void test_parkUntil_3() throws Exception { + CyclicBarrier barrier = new CyclicBarrier(1); + Parker parker = new Parker(barrier, true, 1000); + Thread parkerThread = new Thread(parker); + + UNSAFE.unpark(parkerThread); + parkerThread.start(); + parker.assertDurationIsInRange(0); + parkerThread.join(); + } + + // TODO: Add more tests. + + /** + * Helper <code>Runnable</code> for tests, which parks for or until + * the indicated value, noting the duration of time actually parked. + */ + private static class Parker implements Runnable { + + private final CyclicBarrier barrier; + + /** whether {@link #amount} is milliseconds to wait in an + * absolute fashion (<code>true</code>) or nanoseconds to wait + * in a relative fashion (<code>false</code>) */ + private final boolean absolute; + + /** amount to wait (see above) */ + private final long amount; + + /** whether the run has completed */ + private boolean completed; + + /** recorded start time */ + private long startMillis; + + /** recorded end time */ + private long endMillis; + + /** + * Construct an instance. + * + * @param absolute whether to use an absolute time or not; in + * either case, this constructor takes a duration to park for + * @param parkMillis the number of milliseconds to be parked + */ + public Parker(CyclicBarrier barrier, boolean absolute, long parkMillis) { + this.barrier = barrier; + this.absolute = absolute; + + // Multiply by 1000000 because parkFor() takes nanoseconds. + this.amount = absolute ? parkMillis : parkMillis * 1000000; + } + + public void run() { + try { + barrier.await(60, TimeUnit.SECONDS); + } catch (Exception e) { + throw new AssertionError(e); + } + boolean absolute = this.absolute; + long amount = this.amount; + long startNanos = System.nanoTime(); + long start = System.currentTimeMillis(); + + if (absolute) { + UNSAFE.park(true, start + amount); + } else { + UNSAFE.park(false, amount); + } + + long endNanos = System.nanoTime(); + + synchronized (this) { + startMillis = startNanos / 1000000; + endMillis = endNanos / 1000000; + completed = true; + notifyAll(); + } + } + + /** + * Wait for the test to complete and return the duration. + * + * @param maxWaitMillis the maximum amount of time to + * wait for the test to complete + * @return the duration in milliseconds + */ + public long getDurationMillis(long maxWaitMillis) { + synchronized (this) { + if (! completed) { + try { + wait(maxWaitMillis); + } catch (InterruptedException ignored) { + } + if (! completed) { + Assert.fail("parker hung for more than " + maxWaitMillis + " ms"); + } + } + + return endMillis - startMillis; + } + } + + /** + * Asserts that the actual duration is within 10% of the + * given expected time. + * + * @param expectedMillis the expected duration, in milliseconds + */ + public void assertDurationIsInRange(long expectedMillis) { + /* + * Allow a bit more slop for the maximum on "expected + * instantaneous" results. + */ + long minimum = (long) ((double) expectedMillis * 0.90); + long maximum = + Math.max((long) ((double) expectedMillis * 1.10), 10); + long waitMillis = Math.max(expectedMillis * 10, 10); + long duration = getDurationMillis(waitMillis); + + if (duration < minimum) { + Assert.fail("expected duration: " + expectedMillis + + " minimum duration: " + minimum + + " actual duration too short: " + duration); + } else if (duration > maximum) { + Assert.fail("expected duration: " + expectedMillis + + " maximum duration: " + maximum + + " actual duration too long: " + duration); + } + } + } + + /** + * Helper <code>Runnable</code> for tests, which waits for the + * specified amount of time and then unparks an indicated thread. + */ + private static class WaitAndUnpark implements Runnable { + private final CyclicBarrier barrier; + private final long waitMillis; + private final Thread thread; + + public WaitAndUnpark(CyclicBarrier barrier, long waitMillis, Thread thread) { + this.barrier = barrier; + this.waitMillis = waitMillis; + this.thread = thread; + } + + public void run() { + try { + barrier.await(60, TimeUnit.SECONDS); + } catch (Exception e) { + throw new AssertionError(e); + } + try { + Thread.sleep(waitMillis); + } catch (InterruptedException ex) { + throw new RuntimeException("shouldn't happen", ex); + } + + UNSAFE.unpark(thread); + } + } +} |