am 011ed31c: Better error messages for zlib errors

* commit '011ed31c4d057d973931fa81a09d8c576a72d82a':
  Better error messages for zlib errors
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index 33f381f..9f67048 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -245,16 +245,16 @@
   description: "UTF-16 and UTF-32 encoders don't write big-endian output on little-endian devices",
   names: [
     "libcore.java.nio.charset.CharsetEncoderTest#test_defaultReplacementBytesUtf_16",
-    "libcore.java.nio.charset.CharsetTest#test_UTF_16",
-    "libcore.java.nio.charset.CharsetTest#test_UTF_32",
-    "libcore.java.nio.charset.CharsetTest#test_UTF_16BE",
-    "libcore.java.nio.charset.CharsetTest#test_UTF_16LE",
-    "libcore.java.nio.charset.CharsetTest#test_UTF_32BE",
-    "libcore.java.nio.charset.CharsetTest#test_UTF_32LE",
-    "libcore.java.nio.charset.CharsetTest#test_x_UTF_16LE_BOM",
-    "libcore.java.nio.charset.CharsetTest#test_X_UTF_32BE_BOM",
-    "libcore.java.nio.charset.CharsetTest#test_X_UTF_32LE_BOM",
-    "libcore.java.nio.charset.OldCharset_MultiByte_UTF_16#test_Encode"
+    "libcore.java.nio.charset.OldCharset_MultiByte_UTF_16#test_Encode",
+    "tests.api.java.nio.charset.CharsetTest#test_UTF_16",
+    "tests.api.java.nio.charset.CharsetTest#test_UTF_16BE",
+    "tests.api.java.nio.charset.CharsetTest#test_UTF_16LE",
+    "tests.api.java.nio.charset.CharsetTest#test_UTF_32",
+    "tests.api.java.nio.charset.CharsetTest#test_UTF_32BE",
+    "tests.api.java.nio.charset.CharsetTest#test_UTF_32LE",
+    "tests.api.java.nio.charset.CharsetTest#test_x_UTF_16LE_BOM",
+    "tests.api.java.nio.charset.CharsetTest#test_X_UTF_32BE_BOM",
+    "tests.api.java.nio.charset.CharsetTest#test_X_UTF_32LE_BOM"
   ],
   bug: 2702411
 },
diff --git a/jsr166-tests/src/test/java/jsr166/AbstractExecutorServiceTest.java b/jsr166-tests/src/test/java/jsr166/AbstractExecutorServiceTest.java
new file mode 100644
index 0000000..ba4cc66
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/AbstractExecutorServiceTest.java
@@ -0,0 +1,604 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import java.security.*;
+
+public class AbstractExecutorServiceTest extends JSR166TestCase {
+
+    /**
+     * A no-frills implementation of AbstractExecutorService, designed
+     * to test the submit methods only.
+     */
+    static class DirectExecutorService extends AbstractExecutorService {
+        public void execute(Runnable r) { r.run(); }
+        public void shutdown() { shutdown = true; }
+        public List<Runnable> shutdownNow() {
+            shutdown = true;
+            return Collections.EMPTY_LIST;
+        }
+        public boolean isShutdown() { return shutdown; }
+        public boolean isTerminated() { return isShutdown(); }
+        public boolean awaitTermination(long timeout, TimeUnit unit) {
+            return isShutdown();
+        }
+        private volatile boolean shutdown = false;
+    }
+
+    /**
+     * execute(runnable) runs it to completion
+     */
+    public void testExecuteRunnable() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        final AtomicBoolean done = new AtomicBoolean(false);
+        CheckedRunnable task = new CheckedRunnable() {
+            public void realRun() {
+                done.set(true);
+            }};
+        Future<?> future = e.submit(task);
+        assertNull(future.get());
+        assertNull(future.get(0, MILLISECONDS));
+        assertTrue(done.get());
+        assertTrue(future.isDone());
+        assertFalse(future.isCancelled());
+    }
+
+    /**
+     * Completed submit(callable) returns result
+     */
+    public void testSubmitCallable() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        Future<String> future = e.submit(new StringTask());
+        String result = future.get();
+        assertSame(TEST_STRING, result);
+    }
+
+    /**
+     * Completed submit(runnable) returns successfully
+     */
+    public void testSubmitRunnable() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        Future<?> future = e.submit(new NoOpRunnable());
+        future.get();
+        assertTrue(future.isDone());
+    }
+
+    /**
+     * Completed submit(runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+        String result = future.get();
+        assertSame(TEST_STRING, result);
+    }
+
+    /**
+     * A submitted privileged action runs to completion
+     */
+    public void testSubmitPrivilegedAction() throws Exception {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                ExecutorService e = new DirectExecutorService();
+                Future future = e.submit(Executors.callable(new PrivilegedAction() {
+                    public Object run() {
+                        return TEST_STRING;
+                    }}));
+
+                assertSame(TEST_STRING, future.get());
+            }};
+
+        runWithPermissions(r,
+                           new RuntimePermission("getClassLoader"),
+                           new RuntimePermission("setContextClassLoader"),
+                           new RuntimePermission("modifyThread"));
+    }
+
+    /**
+     * A submitted privileged exception action runs to completion
+     */
+    public void testSubmitPrivilegedExceptionAction() throws Exception {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                ExecutorService e = new DirectExecutorService();
+                Future future = e.submit(Executors.callable(new PrivilegedExceptionAction() {
+                    public Object run() {
+                        return TEST_STRING;
+                    }}));
+
+                assertSame(TEST_STRING, future.get());
+            }};
+
+        runWithPermissions(r);
+    }
+
+    /**
+     * A submitted failed privileged exception action reports exception
+     */
+    public void testSubmitFailedPrivilegedExceptionAction() throws Exception {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                ExecutorService e = new DirectExecutorService();
+                Future future = e.submit(Executors.callable(new PrivilegedExceptionAction() {
+                    public Object run() throws Exception {
+                        throw new IndexOutOfBoundsException();
+                    }}));
+
+                try {
+                    future.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    assertTrue(success.getCause() instanceof IndexOutOfBoundsException);
+                }}};
+
+        runWithPermissions(r);
+    }
+
+    /**
+     * execute(null runnable) throws NPE
+     */
+    public void testExecuteNullRunnable() {
+        try {
+            ExecutorService e = new DirectExecutorService();
+            e.submit((Runnable) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * submit(null callable) throws NPE
+     */
+    public void testSubmitNullCallable() {
+        try {
+            ExecutorService e = new DirectExecutorService();
+            e.submit((Callable) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * submit(callable).get() throws InterruptedException if interrupted
+     */
+    public void testInterruptedSubmit() throws InterruptedException {
+        final CountDownLatch submitted    = new CountDownLatch(1);
+        final CountDownLatch quittingTime = new CountDownLatch(1);
+        final ExecutorService p
+            = new ThreadPoolExecutor(1,1,60, TimeUnit.SECONDS,
+                                     new ArrayBlockingQueue<Runnable>(10));
+        final Callable<Void> awaiter = new CheckedCallable<Void>() {
+            public Void realCall() throws InterruptedException {
+                quittingTime.await();
+                return null;
+            }};
+        try {
+            Thread t = new Thread(new CheckedInterruptedRunnable() {
+                public void realRun() throws Exception {
+                    Future<Void> future = p.submit(awaiter);
+                    submitted.countDown();
+                    future.get();
+                }});
+            t.start();
+            submitted.await();
+            t.interrupt();
+            t.join();
+        } finally {
+            quittingTime.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * get of submit(callable) throws ExecutionException if callable
+     * throws exception
+     */
+    public void testSubmitEE() throws InterruptedException {
+        ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   60, TimeUnit.SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+
+        Callable c = new Callable() {
+            public Object call() { throw new ArithmeticException(); }};
+
+        try {
+            p.submit(c).get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof ArithmeticException);
+        }
+        joinPool(p);
+    }
+
+    /**
+     * invokeAny(null) throws NPE
+     */
+    public void testInvokeAny1() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            e.invokeAny(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IAE
+     */
+    public void testInvokeAny2() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            e.invokeAny(new ArrayList<Callable<String>>());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NPE if c has null elements
+     */
+    public void testInvokeAny3() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        List<Callable<Long>> l = new ArrayList<Callable<Long>>();
+        l.add(new Callable<Long>() {
+            public Long call() { throw new ArithmeticException(); }});
+        l.add(null);
+        try {
+            e.invokeAny(l);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task in c completes
+     */
+    public void testInvokeAny4() throws InterruptedException {
+        ExecutorService e = new DirectExecutorService();
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        try {
+            e.invokeAny(l);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task in c if at least one completes
+     */
+    public void testInvokeAny5() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NPE
+     */
+    public void testInvokeAll1() throws InterruptedException {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            e.invokeAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws InterruptedException {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NPE if c has null elements
+     */
+    public void testInvokeAll3() throws InterruptedException {
+        ExecutorService e = new DirectExecutorService();
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        l.add(null);
+        try {
+            e.invokeAll(l);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * get of returned element of invokeAll(c) throws exception on failed task
+     */
+    public void testInvokeAll4() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks in c
+     */
+    public void testInvokeAll5() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NPE
+     */
+    public void testTimedInvokeAny1() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(null time unit) throws NPE
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IAE
+     */
+    public void testTimedInvokeAny2() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        List<Callable<Long>> l = new ArrayList<Callable<Long>>();
+        l.add(new Callable<Long>() {
+            public Long call() { throw new ArithmeticException(); }});
+        l.add(null);
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task in c
+     */
+    public void testTimedInvokeAny5() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NPE
+     */
+    public void testTimedInvokeAll1() throws InterruptedException {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(null time unit) throws NPE
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws InterruptedException {
+        ExecutorService e = new DirectExecutorService();
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        try {
+            e.invokeAll(l, MEDIUM_DELAY_MS, null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws InterruptedException {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAll3() throws InterruptedException {
+        ExecutorService e = new DirectExecutorService();
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        l.add(null);
+        try {
+            e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * get of returned element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            List<Future<String>> futures =
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks in c
+     */
+    public void testTimedInvokeAll5() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll cancels tasks not completed by timeout
+     */
+    public void testTimedInvokeAll6() throws InterruptedException {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(Executors.callable(possiblyInterruptedRunnable(2 * SHORT_DELAY_MS), TEST_STRING));
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, SHORT_DELAY_MS, MILLISECONDS);
+            assertEquals(l.size(), futures.size());
+            for (Future future : futures)
+                assertTrue(future.isDone());
+            assertFalse(futures.get(0).isCancelled());
+            assertFalse(futures.get(1).isCancelled());
+            assertTrue(futures.get(2).isCancelled());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/AbstractQueueTest.java b/jsr166-tests/src/test/java/jsr166/AbstractQueueTest.java
new file mode 100644
index 0000000..e74fc3c
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/AbstractQueueTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.AbstractQueue;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class AbstractQueueTest extends JSR166TestCase {
+
+    static class Succeed extends AbstractQueue<Integer> {
+        public boolean offer(Integer x) {
+            if (x == null) throw new NullPointerException();
+            return true;
+        }
+        public Integer peek() { return one; }
+        public Integer poll() { return one; }
+        public int size() { return 0; }
+        public Iterator iterator() { return null; } // not needed
+    }
+
+    static class Fail extends AbstractQueue<Integer> {
+        public boolean offer(Integer x) {
+            if (x == null) throw new NullPointerException();
+            return false;
+        }
+        public Integer peek() { return null; }
+        public Integer poll() { return null; }
+        public int size() { return 0; }
+        public Iterator iterator() { return null; } // not needed
+    }
+
+    /**
+     * add returns true if offer succeeds
+     */
+    public void testAddS() {
+        Succeed q = new Succeed();
+        assertTrue(q.add(two));
+    }
+
+    /**
+     * add throws ISE true if offer fails
+     */
+    public void testAddF() {
+        Fail q = new Fail();
+        try {
+            q.add(one);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * add throws NPE if offer does
+     */
+    public void testAddNPE() {
+        Succeed q = new Succeed();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove returns normally if poll succeeds
+     */
+    public void testRemoveS() {
+        Succeed q = new Succeed();
+        q.remove();
+    }
+
+    /**
+     * remove throws NSEE if poll returns null
+     */
+    public void testRemoveF() {
+        Fail q = new Fail();
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * element returns normally if peek succeeds
+     */
+    public void testElementS() {
+        Succeed q = new Succeed();
+        q.element();
+    }
+
+    /**
+     * element throws NSEE if peek returns null
+     */
+    public void testElementF() {
+        Fail q = new Fail();
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        try {
+            Succeed q = new Succeed();
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        try {
+            Succeed q = new Succeed();
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        try {
+            Succeed q = new Succeed();
+            Integer[] ints = new Integer[SIZE];
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        try {
+            Succeed q = new Succeed();
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i);
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll throws ISE if an add fails
+     */
+    public void testAddAll4() {
+        try {
+            Fail q = new Fail();
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE; ++i)
+                ints[i] = new Integer(i);
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/AbstractQueuedLongSynchronizerTest.java b/jsr166-tests/src/test/java/jsr166/AbstractQueuedLongSynchronizerTest.java
new file mode 100644
index 0000000..d957e61
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/AbstractQueuedLongSynchronizerTest.java
@@ -0,0 +1,1209 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import java.util.concurrent.locks.AbstractQueuedLongSynchronizer;
+import java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject;
+
+public class AbstractQueuedLongSynchronizerTest extends JSR166TestCase {
+
+    /**
+     * A simple mutex class, adapted from the class javadoc.  Exclusive
+     * acquire tests exercise this as a sample user extension.
+     */
+    static class Mutex extends AbstractQueuedLongSynchronizer {
+        /** An eccentric value > 32 bits for locked synchronizer state. */
+        static final long LOCKED = (1L << 63) | (1L << 15);
+
+        static final long UNLOCKED = 0;
+
+        public boolean isHeldExclusively() {
+            long state = getState();
+            assertTrue(state == UNLOCKED || state == LOCKED);
+            return state == LOCKED;
+        }
+
+        public boolean tryAcquire(long acquires) {
+            assertEquals(LOCKED, acquires);
+            return compareAndSetState(UNLOCKED, LOCKED);
+        }
+
+        public boolean tryRelease(long releases) {
+            if (getState() != LOCKED) throw new IllegalMonitorStateException();
+            setState(UNLOCKED);
+            return true;
+        }
+
+        public boolean tryAcquireNanos(long nanos) throws InterruptedException {
+            return tryAcquireNanos(LOCKED, nanos);
+        }
+
+        public boolean tryAcquire() {
+            return tryAcquire(LOCKED);
+        }
+
+        public boolean tryRelease() {
+            return tryRelease(LOCKED);
+        }
+
+        public void acquire() {
+            acquire(LOCKED);
+        }
+
+        public void acquireInterruptibly() throws InterruptedException {
+            acquireInterruptibly(LOCKED);
+        }
+
+        public void release() {
+            release(LOCKED);
+        }
+
+        public ConditionObject newCondition() {
+            return new ConditionObject();
+        }
+    }
+
+    /**
+     * A simple latch class, to test shared mode.
+     */
+    static class BooleanLatch extends AbstractQueuedLongSynchronizer {
+        public boolean isSignalled() { return getState() != 0; }
+
+        public long tryAcquireShared(long ignore) {
+            return isSignalled() ? 1 : -1;
+        }
+
+        public boolean tryReleaseShared(long ignore) {
+            setState(1 << 62);
+            return true;
+        }
+    }
+
+    /**
+     * A runnable calling acquireInterruptibly that does not expect to
+     * be interrupted.
+     */
+    class InterruptibleSyncRunnable extends CheckedRunnable {
+        final Mutex sync;
+        InterruptibleSyncRunnable(Mutex sync) { this.sync = sync; }
+        public void realRun() throws InterruptedException {
+            sync.acquireInterruptibly();
+        }
+    }
+
+    /**
+     * A runnable calling acquireInterruptibly that expects to be
+     * interrupted.
+     */
+    class InterruptedSyncRunnable extends CheckedInterruptedRunnable {
+        final Mutex sync;
+        InterruptedSyncRunnable(Mutex sync) { this.sync = sync; }
+        public void realRun() throws InterruptedException {
+            sync.acquireInterruptibly();
+        }
+    }
+
+    /** A constant to clarify calls to checking methods below. */
+    static final Thread[] NO_THREADS = new Thread[0];
+
+    /**
+     * Spin-waits until sync.isQueued(t) becomes true.
+     */
+    void waitForQueuedThread(AbstractQueuedLongSynchronizer sync,
+                             Thread t) {
+        long startTime = System.nanoTime();
+        while (!sync.isQueued(t)) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                throw new AssertionFailedError("timed out");
+            Thread.yield();
+        }
+        assertTrue(t.isAlive());
+    }
+
+    /**
+     * Checks that sync has exactly the given queued threads.
+     */
+    void assertHasQueuedThreads(AbstractQueuedLongSynchronizer sync,
+                                Thread... expected) {
+        Collection<Thread> actual = sync.getQueuedThreads();
+        assertEquals(expected.length > 0, sync.hasQueuedThreads());
+        assertEquals(expected.length, sync.getQueueLength());
+        assertEquals(expected.length, actual.size());
+        assertEquals(expected.length == 0, actual.isEmpty());
+        assertEquals(new HashSet<Thread>(actual),
+                     new HashSet<Thread>(Arrays.asList(expected)));
+    }
+
+    /**
+     * Checks that sync has exactly the given (exclusive) queued threads.
+     */
+    void assertHasExclusiveQueuedThreads(AbstractQueuedLongSynchronizer sync,
+                                         Thread... expected) {
+        assertHasQueuedThreads(sync, expected);
+        assertEquals(new HashSet<Thread>(sync.getExclusiveQueuedThreads()),
+                     new HashSet<Thread>(sync.getQueuedThreads()));
+        assertEquals(0, sync.getSharedQueuedThreads().size());
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+    }
+
+    /**
+     * Checks that sync has exactly the given (shared) queued threads.
+     */
+    void assertHasSharedQueuedThreads(AbstractQueuedLongSynchronizer sync,
+                                      Thread... expected) {
+        assertHasQueuedThreads(sync, expected);
+        assertEquals(new HashSet<Thread>(sync.getSharedQueuedThreads()),
+                     new HashSet<Thread>(sync.getQueuedThreads()));
+        assertEquals(0, sync.getExclusiveQueuedThreads().size());
+        assertTrue(sync.getExclusiveQueuedThreads().isEmpty());
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads,
+     * after acquiring mutex.
+     */
+    void assertHasWaitersUnlocked(Mutex sync, ConditionObject c,
+                                 Thread... threads) {
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, threads);
+        sync.release();
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads.
+     */
+    void assertHasWaitersLocked(Mutex sync, ConditionObject c,
+                                Thread... threads) {
+        assertEquals(threads.length > 0, sync.hasWaiters(c));
+        assertEquals(threads.length, sync.getWaitQueueLength(c));
+        assertEquals(threads.length == 0, sync.getWaitingThreads(c).isEmpty());
+        assertEquals(threads.length, sync.getWaitingThreads(c).size());
+        assertEquals(new HashSet<Thread>(sync.getWaitingThreads(c)),
+                     new HashSet<Thread>(Arrays.asList(threads)));
+    }
+
+    enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil };
+
+    /**
+     * Awaits condition using the specified AwaitMethod.
+     */
+    void await(ConditionObject c, AwaitMethod awaitMethod)
+            throws InterruptedException {
+        long timeoutMillis = 2 * LONG_DELAY_MS;
+        switch (awaitMethod) {
+        case await:
+            c.await();
+            break;
+        case awaitTimed:
+            assertTrue(c.await(timeoutMillis, MILLISECONDS));
+            break;
+        case awaitNanos:
+            long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis);
+            long nanosRemaining = c.awaitNanos(nanosTimeout);
+            assertTrue(nanosRemaining > 0);
+            break;
+        case awaitUntil:
+            assertTrue(c.awaitUntil(delayedDate(timeoutMillis)));
+            break;
+        }
+    }
+
+    /**
+     * Checks that awaiting the given condition times out (using the
+     * default timeout duration).
+     */
+    void assertAwaitTimesOut(ConditionObject c, AwaitMethod awaitMethod) {
+        long timeoutMillis = timeoutMillis();
+        long startTime = System.nanoTime();
+        try {
+            switch (awaitMethod) {
+            case awaitTimed:
+                assertFalse(c.await(timeoutMillis, MILLISECONDS));
+                break;
+            case awaitNanos:
+                long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis);
+                long nanosRemaining = c.awaitNanos(nanosTimeout);
+                assertTrue(nanosRemaining <= 0);
+                break;
+            case awaitUntil:
+                assertFalse(c.awaitUntil(delayedDate(timeoutMillis)));
+                break;
+            default:
+                throw new UnsupportedOperationException();
+            }
+        } catch (InterruptedException ie) { threadUnexpectedException(ie); }
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+    }
+
+    /**
+     * isHeldExclusively is false upon construction
+     */
+    public void testIsHeldExclusively() {
+        Mutex sync = new Mutex();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * acquiring released sync succeeds
+     */
+    public void testAcquire() {
+        Mutex sync = new Mutex();
+        sync.acquire();
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * tryAcquire on a released sync succeeds
+     */
+    public void testTryAcquire() {
+        Mutex sync = new Mutex();
+        assertTrue(sync.tryAcquire());
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * hasQueuedThreads reports whether there are waiting threads
+     */
+    public void testHasQueuedThreads() {
+        final Mutex sync = new Mutex();
+        assertFalse(sync.hasQueuedThreads());
+        sync.acquire();
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.hasQueuedThreads());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.hasQueuedThreads());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.hasQueuedThreads());
+        sync.release();
+        awaitTermination(t2);
+        assertFalse(sync.hasQueuedThreads());
+    }
+
+    /**
+     * isQueued(null) throws NullPointerException
+     */
+    public void testIsQueuedNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.isQueued(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * isQueued reports whether a thread is queued
+     */
+    public void testIsQueued() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertFalse(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+        sync.acquire();
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.isQueued(t1));
+        assertTrue(sync.isQueued(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(sync.isQueued(t1));
+        assertTrue(sync.isQueued(t2));
+        sync.release();
+        awaitTermination(t2);
+        assertFalse(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+    }
+
+    /**
+     * getFirstQueuedThread returns first waiting thread or null if none
+     */
+    public void testGetFirstQueuedThread() {
+        final Mutex sync = new Mutex();
+        assertNull(sync.getFirstQueuedThread());
+        sync.acquire();
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertEquals(t1, sync.getFirstQueuedThread());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertEquals(t1, sync.getFirstQueuedThread());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertEquals(t2, sync.getFirstQueuedThread());
+        sync.release();
+        awaitTermination(t2);
+        assertNull(sync.getFirstQueuedThread());
+    }
+
+    /**
+     * hasContended reports false if no thread has ever blocked, else true
+     */
+    public void testHasContended() {
+        final Mutex sync = new Mutex();
+        assertFalse(sync.hasContended());
+        sync.acquire();
+        assertFalse(sync.hasContended());
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.hasContended());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.hasContended());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.hasContended());
+        sync.release();
+        awaitTermination(t2);
+        assertTrue(sync.hasContended());
+    }
+
+    /**
+     * getQueuedThreads returns all waiting threads
+     */
+    public void testGetQueuedThreads() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        sync.acquire();
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertHasExclusiveQueuedThreads(sync, t1);
+        assertTrue(sync.getQueuedThreads().contains(t1));
+        assertFalse(sync.getQueuedThreads().contains(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertTrue(sync.getQueuedThreads().contains(t1));
+        assertTrue(sync.getQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasExclusiveQueuedThreads(sync, t2);
+        sync.release();
+        awaitTermination(t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+    }
+
+    /**
+     * getExclusiveQueuedThreads returns all exclusive waiting threads
+     */
+    public void testGetExclusiveQueuedThreads() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        sync.acquire();
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertHasExclusiveQueuedThreads(sync, t1);
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t1));
+        assertFalse(sync.getExclusiveQueuedThreads().contains(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t1));
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasExclusiveQueuedThreads(sync, t2);
+        sync.release();
+        awaitTermination(t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+    }
+
+    /**
+     * getSharedQueuedThreads does not include exclusively waiting threads
+     */
+    public void testGetSharedQueuedThreads_Exclusive() {
+        final Mutex sync = new Mutex();
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        sync.acquire();
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        sync.release();
+        awaitTermination(t2);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+    }
+
+    /**
+     * getSharedQueuedThreads returns all shared waiting threads
+     */
+    public void testGetSharedQueuedThreads_Shared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertHasSharedQueuedThreads(l, NO_THREADS);
+        Thread t1 = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                l.acquireSharedInterruptibly(0);
+            }});
+        waitForQueuedThread(l, t1);
+        assertHasSharedQueuedThreads(l, t1);
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                l.acquireSharedInterruptibly(0);
+            }});
+        waitForQueuedThread(l, t2);
+        assertHasSharedQueuedThreads(l, t1, t2);
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasSharedQueuedThreads(l, t2);
+        assertTrue(l.releaseShared(0));
+        awaitTermination(t2);
+        assertHasSharedQueuedThreads(l, NO_THREADS);
+    }
+
+    /**
+     * tryAcquireNanos is interruptible
+     */
+    public void testTryAcquireNanos_Interruptible() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.tryAcquireNanos(MILLISECONDS.toNanos(2 * LONG_DELAY_MS));
+            }});
+
+        waitForQueuedThread(sync, t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * tryAcquire on exclusively held sync fails
+     */
+    public void testTryAcquireWhenSynced() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(sync.tryAcquire());
+            }});
+
+        awaitTermination(t);
+        sync.release();
+    }
+
+    /**
+     * tryAcquireNanos on an exclusively held sync times out
+     */
+    public void testAcquireNanos_Timeout() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                long nanos = MILLISECONDS.toNanos(timeoutMillis());
+                assertFalse(sync.tryAcquireNanos(nanos));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t);
+        sync.release();
+    }
+
+    /**
+     * getState is true when acquired and false when not
+     */
+    public void testGetState() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+
+        final BooleanLatch acquired = new BooleanLatch();
+        final BooleanLatch done = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(acquired.releaseShared(0));
+                done.acquireShared(0);
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        assertTrue(sync.isHeldExclusively());
+        assertTrue(done.releaseShared(0));
+        awaitTermination(t);
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * acquireInterruptibly succeeds when released, else is interruptible
+     */
+    public void testAcquireInterruptibly() throws InterruptedException {
+        final Mutex sync = new Mutex();
+        final BooleanLatch threadStarted = new BooleanLatch();
+        sync.acquireInterruptibly();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertTrue(threadStarted.releaseShared(0));
+                sync.acquireInterruptibly();
+            }});
+
+        threadStarted.acquireShared(0);
+        waitForQueuedThread(sync, t);
+        t.interrupt();
+        awaitTermination(t);
+        assertTrue(sync.isHeldExclusively());
+    }
+
+    /**
+     * owns is true for a condition created by sync else false
+     */
+    public void testOwns() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        assertTrue(sync.owns(c));
+        assertFalse(sync2.owns(c));
+    }
+
+    /**
+     * Calling await without holding sync throws IllegalMonitorStateException
+     */
+    public void testAwait_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        for (AwaitMethod awaitMethod : AwaitMethod.values()) {
+            long startTime = System.nanoTime();
+            try {
+                await(c, awaitMethod);
+                shouldThrow();
+            } catch (IllegalMonitorStateException success) {
+            } catch (InterruptedException e) { threadUnexpectedException(e); }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * Calling signal without holding sync throws IllegalMonitorStateException
+     */
+    public void testSignal_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            c.signal();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * Calling signalAll without holding sync throws IllegalMonitorStateException
+     */
+    public void testSignalAll_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            c.signalAll();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil without a signal times out
+     */
+    public void testAwaitTimed_Timeout() { testAwait_Timeout(AwaitMethod.awaitTimed); }
+    public void testAwaitNanos_Timeout() { testAwait_Timeout(AwaitMethod.awaitNanos); }
+    public void testAwaitUntil_Timeout() { testAwait_Timeout(AwaitMethod.awaitUntil); }
+    public void testAwait_Timeout(AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        sync.acquire();
+        assertAwaitTimesOut(c, awaitMethod);
+        sync.release();
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil returns when signalled
+     */
+    public void testSignal_await()      { testSignal(AwaitMethod.await); }
+    public void testSignal_awaitTimed() { testSignal(AwaitMethod.awaitTimed); }
+    public void testSignal_awaitNanos() { testSignal(AwaitMethod.awaitNanos); }
+    public void testSignal_awaitUntil() { testSignal(AwaitMethod.awaitUntil); }
+    public void testSignal(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(acquired.releaseShared(0));
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        sync.release();
+        awaitTermination(t);
+    }
+
+    /**
+     * hasWaiters(null) throws NullPointerException
+     */
+    public void testHasWaitersNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.hasWaiters(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitQueueLength(null) throws NullPointerException
+     */
+    public void testGetWaitQueueLengthNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.getWaitQueueLength(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws NPE if null
+     */
+    public void testGetWaitingThreadsNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.getWaitingThreads(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalArgumentException if not owned
+     */
+    public void testHasWaitersIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * hasWaiters throws IllegalMonitorStateException if not synced
+     */
+    public void testHasWaitersIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitQueueLengthIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalMonitorStateException if not synced
+     */
+    public void testGetWaitQueueLengthIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitingThreadsIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads throws IllegalMonitorStateException if not synced
+     */
+    public void testGetWaitingThreadsIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * hasWaiters returns true when a thread is waiting, else false
+     */
+    public void testHasWaiters() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertFalse(sync.hasWaiters(c));
+                assertTrue(acquired.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertTrue(sync.hasWaiters(c));
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        assertFalse(sync.hasWaiters(c));
+        sync.release();
+
+        awaitTermination(t);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength returns number of waiting threads
+     */
+    public void testGetWaitQueueLength() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        final Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertEquals(0, sync.getWaitQueueLength(c));
+                assertTrue(acquired1.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+        acquired1.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1);
+        assertEquals(1, sync.getWaitQueueLength(c));
+        sync.release();
+
+        final Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, t1);
+                assertEquals(1, sync.getWaitQueueLength(c));
+                assertTrue(acquired2.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertEquals(2, sync.getWaitQueueLength(c));
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertEquals(0, sync.getWaitQueueLength(c));
+        sync.release();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads returns only and all waiting threads
+     */
+    public void testGetWaitingThreads() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        final Thread t1 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertTrue(sync.getWaitingThreads(c).isEmpty());
+                assertTrue(acquired1.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        final Thread t2 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, t1);
+                assertTrue(sync.getWaitingThreads(c).contains(t1));
+                assertFalse(sync.getWaitingThreads(c).isEmpty());
+                assertEquals(1, sync.getWaitingThreads(c).size());
+                assertTrue(acquired2.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertFalse(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertTrue(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(0, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        t1.start();
+        acquired1.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1);
+        assertTrue(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertFalse(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(1, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        t2.start();
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertTrue(sync.getWaitingThreads(c).contains(t1));
+        assertTrue(sync.getWaitingThreads(c).contains(t2));
+        assertFalse(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(2, sync.getWaitingThreads(c).size());
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertFalse(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertTrue(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(0, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * awaitUninterruptibly is uninterruptible
+     */
+    public void testAwaitUninterruptibly() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch pleaseInterrupt = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                sync.acquire();
+                assertTrue(pleaseInterrupt.releaseShared(0));
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                sync.release();
+            }});
+
+        pleaseInterrupt.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        sync.release();
+        t.interrupt();
+        assertHasWaitersUnlocked(sync, c, t);
+        assertThreadStaysAlive(t);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        sync.release();
+        awaitTermination(t);
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil is interruptible
+     */
+    public void testInterruptible_await()      { testInterruptible(AwaitMethod.await); }
+    public void testInterruptible_awaitTimed() { testInterruptible(AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitNanos() { testInterruptible(AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitUntil() { testInterruptible(AwaitMethod.awaitUntil); }
+    public void testInterruptible(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch pleaseInterrupt = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(pleaseInterrupt.releaseShared(0));
+                await(c, awaitMethod);
+            }});
+
+        pleaseInterrupt.acquireShared(0);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * signalAll wakes up all threads
+     */
+    public void testSignalAll_await()      { testSignalAll(AwaitMethod.await); }
+    public void testSignalAll_awaitTimed() { testSignalAll(AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitNanos() { testSignalAll(AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitUntil() { testSignalAll(AwaitMethod.awaitUntil); }
+    public void testSignalAll(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                acquired1.releaseShared(0);
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                acquired2.releaseShared(0);
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        acquired1.acquireShared(0);
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        sync.release();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * toString indicates current state
+     */
+    public void testToString() {
+        Mutex sync = new Mutex();
+        assertTrue(sync.toString().contains("State = " + Mutex.UNLOCKED));
+        sync.acquire();
+        assertTrue(sync.toString().contains("State = " + Mutex.LOCKED));
+    }
+
+    /**
+     * A serialized AQS deserializes with current state, but no queued threads
+     */
+    public void testSerialization() {
+        Mutex sync = new Mutex();
+        assertFalse(serialClone(sync).isHeldExclusively());
+        sync.acquire();
+        Thread t = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t);
+        assertTrue(sync.isHeldExclusively());
+
+        Mutex clone = serialClone(sync);
+        assertTrue(clone.isHeldExclusively());
+        assertHasExclusiveQueuedThreads(sync, t);
+        assertHasExclusiveQueuedThreads(clone, NO_THREADS);
+        t.interrupt();
+        awaitTermination(t);
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+        assertTrue(clone.isHeldExclusively());
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertHasExclusiveQueuedThreads(clone, NO_THREADS);
+    }
+
+    /**
+     * tryReleaseShared setting state changes getState
+     */
+    public void testGetStateWithReleaseShared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertFalse(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+    }
+
+    /**
+     * releaseShared has no effect when already signalled
+     */
+    public void testReleaseShared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertFalse(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+    }
+
+    /**
+     * acquireSharedInterruptibly returns after release, but not before
+     */
+    public void testAcquireSharedInterruptibly() {
+        final BooleanLatch l = new BooleanLatch();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+                assertTrue(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+                assertTrue(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        assertThreadStaysAlive(t);
+        assertHasSharedQueuedThreads(l, t);
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        awaitTermination(t);
+    }
+
+    /**
+     * tryAcquireSharedNanos returns after release, but not before
+     */
+    public void testTryAcquireSharedNanos() {
+        final BooleanLatch l = new BooleanLatch();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                long nanos = MILLISECONDS.toNanos(2 * LONG_DELAY_MS);
+                assertTrue(l.tryAcquireSharedNanos(0, nanos));
+                assertTrue(l.isSignalled());
+                assertTrue(l.tryAcquireSharedNanos(0, nanos));
+                assertTrue(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        assertThreadStaysAlive(t);
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        awaitTermination(t);
+    }
+
+    /**
+     * acquireSharedInterruptibly is interruptible
+     */
+    public void testAcquireSharedInterruptibly_Interruptible() {
+        final BooleanLatch l = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        t.interrupt();
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+    /**
+     * tryAcquireSharedNanos is interruptible
+     */
+    public void testTryAcquireSharedNanos_Interruptible() {
+        final BooleanLatch l = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                long nanos = MILLISECONDS.toNanos(2 * LONG_DELAY_MS);
+                l.tryAcquireSharedNanos(0, nanos);
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        t.interrupt();
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+    /**
+     * tryAcquireSharedNanos times out if not released before timeout
+     */
+    public void testTryAcquireSharedNanos_Timeout() {
+        final BooleanLatch l = new BooleanLatch();
+        final BooleanLatch observedQueued = new BooleanLatch();
+        final long timeoutMillis = timeoutMillis();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                for (long millis = timeoutMillis();
+                     !observedQueued.isSignalled();
+                     millis *= 2) {
+                    long nanos = MILLISECONDS.toNanos(millis);
+                    long startTime = System.nanoTime();
+                    assertFalse(l.tryAcquireSharedNanos(0, nanos));
+                    assertTrue(millisElapsedSince(startTime) >= millis);
+                }
+                assertFalse(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        observedQueued.releaseShared(0);
+        assertFalse(l.isSignalled());
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/AbstractQueuedSynchronizerTest.java b/jsr166-tests/src/test/java/jsr166/AbstractQueuedSynchronizerTest.java
new file mode 100644
index 0000000..b9dab06
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/AbstractQueuedSynchronizerTest.java
@@ -0,0 +1,1212 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import java.util.concurrent.locks.AbstractQueuedSynchronizer;
+import java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject;
+
+public class AbstractQueuedSynchronizerTest extends JSR166TestCase {
+
+    /**
+     * A simple mutex class, adapted from the class javadoc.  Exclusive
+     * acquire tests exercise this as a sample user extension.  Other
+     * methods/features of AbstractQueuedSynchronizer are tested via
+     * other test classes, including those for ReentrantLock,
+     * ReentrantReadWriteLock, and Semaphore.
+     */
+    static class Mutex extends AbstractQueuedSynchronizer {
+        /** An eccentric value for locked synchronizer state. */
+        static final int LOCKED = (1 << 31) | (1 << 15);
+
+        static final int UNLOCKED = 0;
+
+        @Override public boolean isHeldExclusively() {
+            int state = getState();
+            assertTrue(state == UNLOCKED || state == LOCKED);
+            return state == LOCKED;
+        }
+
+        @Override public boolean tryAcquire(int acquires) {
+            assertEquals(LOCKED, acquires);
+            return compareAndSetState(UNLOCKED, LOCKED);
+        }
+
+        @Override public boolean tryRelease(int releases) {
+            if (getState() != LOCKED) throw new IllegalMonitorStateException();
+            assertEquals(LOCKED, releases);
+            setState(UNLOCKED);
+            return true;
+        }
+
+        public boolean tryAcquireNanos(long nanos) throws InterruptedException {
+            return tryAcquireNanos(LOCKED, nanos);
+        }
+
+        public boolean tryAcquire() {
+            return tryAcquire(LOCKED);
+        }
+
+        public boolean tryRelease() {
+            return tryRelease(LOCKED);
+        }
+
+        public void acquire() {
+            acquire(LOCKED);
+        }
+
+        public void acquireInterruptibly() throws InterruptedException {
+            acquireInterruptibly(LOCKED);
+        }
+
+        public void release() {
+            release(LOCKED);
+        }
+
+        public ConditionObject newCondition() {
+            return new ConditionObject();
+        }
+    }
+
+    /**
+     * A simple latch class, to test shared mode.
+     */
+    static class BooleanLatch extends AbstractQueuedSynchronizer {
+        public boolean isSignalled() { return getState() != 0; }
+
+        public int tryAcquireShared(int ignore) {
+            return isSignalled() ? 1 : -1;
+        }
+
+        public boolean tryReleaseShared(int ignore) {
+            setState(1);
+            return true;
+        }
+    }
+
+    /**
+     * A runnable calling acquireInterruptibly that does not expect to
+     * be interrupted.
+     */
+    class InterruptibleSyncRunnable extends CheckedRunnable {
+        final Mutex sync;
+        InterruptibleSyncRunnable(Mutex sync) { this.sync = sync; }
+        public void realRun() throws InterruptedException {
+            sync.acquireInterruptibly();
+        }
+    }
+
+    /**
+     * A runnable calling acquireInterruptibly that expects to be
+     * interrupted.
+     */
+    class InterruptedSyncRunnable extends CheckedInterruptedRunnable {
+        final Mutex sync;
+        InterruptedSyncRunnable(Mutex sync) { this.sync = sync; }
+        public void realRun() throws InterruptedException {
+            sync.acquireInterruptibly();
+        }
+    }
+
+    /** A constant to clarify calls to checking methods below. */
+    static final Thread[] NO_THREADS = new Thread[0];
+
+    /**
+     * Spin-waits until sync.isQueued(t) becomes true.
+     */
+    void waitForQueuedThread(AbstractQueuedSynchronizer sync, Thread t) {
+        long startTime = System.nanoTime();
+        while (!sync.isQueued(t)) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                throw new AssertionFailedError("timed out");
+            Thread.yield();
+        }
+        assertTrue(t.isAlive());
+    }
+
+    /**
+     * Checks that sync has exactly the given queued threads.
+     */
+    void assertHasQueuedThreads(AbstractQueuedSynchronizer sync,
+                                Thread... expected) {
+        Collection<Thread> actual = sync.getQueuedThreads();
+        assertEquals(expected.length > 0, sync.hasQueuedThreads());
+        assertEquals(expected.length, sync.getQueueLength());
+        assertEquals(expected.length, actual.size());
+        assertEquals(expected.length == 0, actual.isEmpty());
+        assertEquals(new HashSet<Thread>(actual),
+                     new HashSet<Thread>(Arrays.asList(expected)));
+    }
+
+    /**
+     * Checks that sync has exactly the given (exclusive) queued threads.
+     */
+    void assertHasExclusiveQueuedThreads(AbstractQueuedSynchronizer sync,
+                                         Thread... expected) {
+        assertHasQueuedThreads(sync, expected);
+        assertEquals(new HashSet<Thread>(sync.getExclusiveQueuedThreads()),
+                     new HashSet<Thread>(sync.getQueuedThreads()));
+        assertEquals(0, sync.getSharedQueuedThreads().size());
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+    }
+
+    /**
+     * Checks that sync has exactly the given (shared) queued threads.
+     */
+    void assertHasSharedQueuedThreads(AbstractQueuedSynchronizer sync,
+                                      Thread... expected) {
+        assertHasQueuedThreads(sync, expected);
+        assertEquals(new HashSet<Thread>(sync.getSharedQueuedThreads()),
+                     new HashSet<Thread>(sync.getQueuedThreads()));
+        assertEquals(0, sync.getExclusiveQueuedThreads().size());
+        assertTrue(sync.getExclusiveQueuedThreads().isEmpty());
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads,
+     * after acquiring mutex.
+     */
+    void assertHasWaitersUnlocked(Mutex sync, ConditionObject c,
+                                 Thread... threads) {
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, threads);
+        sync.release();
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads.
+     */
+    void assertHasWaitersLocked(Mutex sync, ConditionObject c,
+                                Thread... threads) {
+        assertEquals(threads.length > 0, sync.hasWaiters(c));
+        assertEquals(threads.length, sync.getWaitQueueLength(c));
+        assertEquals(threads.length == 0, sync.getWaitingThreads(c).isEmpty());
+        assertEquals(threads.length, sync.getWaitingThreads(c).size());
+        assertEquals(new HashSet<Thread>(sync.getWaitingThreads(c)),
+                     new HashSet<Thread>(Arrays.asList(threads)));
+    }
+
+    enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil };
+
+    /**
+     * Awaits condition using the specified AwaitMethod.
+     */
+    void await(ConditionObject c, AwaitMethod awaitMethod)
+            throws InterruptedException {
+        long timeoutMillis = 2 * LONG_DELAY_MS;
+        switch (awaitMethod) {
+        case await:
+            c.await();
+            break;
+        case awaitTimed:
+            assertTrue(c.await(timeoutMillis, MILLISECONDS));
+            break;
+        case awaitNanos:
+            long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis);
+            long nanosRemaining = c.awaitNanos(nanosTimeout);
+            assertTrue(nanosRemaining > 0);
+            break;
+        case awaitUntil:
+            assertTrue(c.awaitUntil(delayedDate(timeoutMillis)));
+            break;
+        }
+    }
+
+    /**
+     * Checks that awaiting the given condition times out (using the
+     * default timeout duration).
+     */
+    void assertAwaitTimesOut(ConditionObject c, AwaitMethod awaitMethod) {
+        long timeoutMillis = timeoutMillis();
+        long startTime = System.nanoTime();
+        try {
+            switch (awaitMethod) {
+            case awaitTimed:
+                assertFalse(c.await(timeoutMillis, MILLISECONDS));
+                break;
+            case awaitNanos:
+                long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis);
+                long nanosRemaining = c.awaitNanos(nanosTimeout);
+                assertTrue(nanosRemaining <= 0);
+                break;
+            case awaitUntil:
+                assertFalse(c.awaitUntil(delayedDate(timeoutMillis)));
+                break;
+            default:
+                throw new UnsupportedOperationException();
+            }
+        } catch (InterruptedException ie) { threadUnexpectedException(ie); }
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+    }
+
+    /**
+     * isHeldExclusively is false upon construction
+     */
+    public void testIsHeldExclusively() {
+        Mutex sync = new Mutex();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * acquiring released sync succeeds
+     */
+    public void testAcquire() {
+        Mutex sync = new Mutex();
+        sync.acquire();
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * tryAcquire on a released sync succeeds
+     */
+    public void testTryAcquire() {
+        Mutex sync = new Mutex();
+        assertTrue(sync.tryAcquire());
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * hasQueuedThreads reports whether there are waiting threads
+     */
+    public void testHasQueuedThreads() {
+        final Mutex sync = new Mutex();
+        assertFalse(sync.hasQueuedThreads());
+        sync.acquire();
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.hasQueuedThreads());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.hasQueuedThreads());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.hasQueuedThreads());
+        sync.release();
+        awaitTermination(t2);
+        assertFalse(sync.hasQueuedThreads());
+    }
+
+    /**
+     * isQueued(null) throws NullPointerException
+     */
+    public void testIsQueuedNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.isQueued(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * isQueued reports whether a thread is queued
+     */
+    public void testIsQueued() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertFalse(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+        sync.acquire();
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.isQueued(t1));
+        assertTrue(sync.isQueued(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(sync.isQueued(t1));
+        assertTrue(sync.isQueued(t2));
+        sync.release();
+        awaitTermination(t2);
+        assertFalse(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+    }
+
+    /**
+     * getFirstQueuedThread returns first waiting thread or null if none
+     */
+    public void testGetFirstQueuedThread() {
+        final Mutex sync = new Mutex();
+        assertNull(sync.getFirstQueuedThread());
+        sync.acquire();
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertEquals(t1, sync.getFirstQueuedThread());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertEquals(t1, sync.getFirstQueuedThread());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertEquals(t2, sync.getFirstQueuedThread());
+        sync.release();
+        awaitTermination(t2);
+        assertNull(sync.getFirstQueuedThread());
+    }
+
+    /**
+     * hasContended reports false if no thread has ever blocked, else true
+     */
+    public void testHasContended() {
+        final Mutex sync = new Mutex();
+        assertFalse(sync.hasContended());
+        sync.acquire();
+        assertFalse(sync.hasContended());
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.hasContended());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.hasContended());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.hasContended());
+        sync.release();
+        awaitTermination(t2);
+        assertTrue(sync.hasContended());
+    }
+
+    /**
+     * getQueuedThreads returns all waiting threads
+     */
+    public void testGetQueuedThreads() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        sync.acquire();
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertHasExclusiveQueuedThreads(sync, t1);
+        assertTrue(sync.getQueuedThreads().contains(t1));
+        assertFalse(sync.getQueuedThreads().contains(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertTrue(sync.getQueuedThreads().contains(t1));
+        assertTrue(sync.getQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasExclusiveQueuedThreads(sync, t2);
+        sync.release();
+        awaitTermination(t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+    }
+
+    /**
+     * getExclusiveQueuedThreads returns all exclusive waiting threads
+     */
+    public void testGetExclusiveQueuedThreads() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        sync.acquire();
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertHasExclusiveQueuedThreads(sync, t1);
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t1));
+        assertFalse(sync.getExclusiveQueuedThreads().contains(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t1));
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasExclusiveQueuedThreads(sync, t2);
+        sync.release();
+        awaitTermination(t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+    }
+
+    /**
+     * getSharedQueuedThreads does not include exclusively waiting threads
+     */
+    public void testGetSharedQueuedThreads_Exclusive() {
+        final Mutex sync = new Mutex();
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        sync.acquire();
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        sync.release();
+        awaitTermination(t2);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+    }
+
+    /**
+     * getSharedQueuedThreads returns all shared waiting threads
+     */
+    public void testGetSharedQueuedThreads_Shared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertHasSharedQueuedThreads(l, NO_THREADS);
+        Thread t1 = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                l.acquireSharedInterruptibly(0);
+            }});
+        waitForQueuedThread(l, t1);
+        assertHasSharedQueuedThreads(l, t1);
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                l.acquireSharedInterruptibly(0);
+            }});
+        waitForQueuedThread(l, t2);
+        assertHasSharedQueuedThreads(l, t1, t2);
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasSharedQueuedThreads(l, t2);
+        assertTrue(l.releaseShared(0));
+        awaitTermination(t2);
+        assertHasSharedQueuedThreads(l, NO_THREADS);
+    }
+
+    /**
+     * tryAcquireNanos is interruptible
+     */
+    public void testTryAcquireNanos_Interruptible() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.tryAcquireNanos(MILLISECONDS.toNanos(2 * LONG_DELAY_MS));
+            }});
+
+        waitForQueuedThread(sync, t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * tryAcquire on exclusively held sync fails
+     */
+    public void testTryAcquireWhenSynced() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(sync.tryAcquire());
+            }});
+
+        awaitTermination(t);
+        sync.release();
+    }
+
+    /**
+     * tryAcquireNanos on an exclusively held sync times out
+     */
+    public void testAcquireNanos_Timeout() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                long nanos = MILLISECONDS.toNanos(timeoutMillis());
+                assertFalse(sync.tryAcquireNanos(nanos));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t);
+        sync.release();
+    }
+
+    /**
+     * getState is true when acquired and false when not
+     */
+    public void testGetState() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+
+        final BooleanLatch acquired = new BooleanLatch();
+        final BooleanLatch done = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(acquired.releaseShared(0));
+                done.acquireShared(0);
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        assertTrue(sync.isHeldExclusively());
+        assertTrue(done.releaseShared(0));
+        awaitTermination(t);
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * acquireInterruptibly succeeds when released, else is interruptible
+     */
+    public void testAcquireInterruptibly() throws InterruptedException {
+        final Mutex sync = new Mutex();
+        final BooleanLatch threadStarted = new BooleanLatch();
+        sync.acquireInterruptibly();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertTrue(threadStarted.releaseShared(0));
+                sync.acquireInterruptibly();
+            }});
+
+        threadStarted.acquireShared(0);
+        waitForQueuedThread(sync, t);
+        t.interrupt();
+        awaitTermination(t);
+        assertTrue(sync.isHeldExclusively());
+    }
+
+    /**
+     * owns is true for a condition created by sync else false
+     */
+    public void testOwns() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        assertTrue(sync.owns(c));
+        assertFalse(sync2.owns(c));
+    }
+
+    /**
+     * Calling await without holding sync throws IllegalMonitorStateException
+     */
+    public void testAwait_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        for (AwaitMethod awaitMethod : AwaitMethod.values()) {
+            long startTime = System.nanoTime();
+            try {
+                await(c, awaitMethod);
+                shouldThrow();
+            } catch (IllegalMonitorStateException success) {
+            } catch (InterruptedException e) { threadUnexpectedException(e); }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * Calling signal without holding sync throws IllegalMonitorStateException
+     */
+    public void testSignal_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            c.signal();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * Calling signalAll without holding sync throws IllegalMonitorStateException
+     */
+    public void testSignalAll_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            c.signalAll();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil without a signal times out
+     */
+    public void testAwaitTimed_Timeout() { testAwait_Timeout(AwaitMethod.awaitTimed); }
+    public void testAwaitNanos_Timeout() { testAwait_Timeout(AwaitMethod.awaitNanos); }
+    public void testAwaitUntil_Timeout() { testAwait_Timeout(AwaitMethod.awaitUntil); }
+    public void testAwait_Timeout(AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        sync.acquire();
+        assertAwaitTimesOut(c, awaitMethod);
+        sync.release();
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil returns when signalled
+     */
+    public void testSignal_await()      { testSignal(AwaitMethod.await); }
+    public void testSignal_awaitTimed() { testSignal(AwaitMethod.awaitTimed); }
+    public void testSignal_awaitNanos() { testSignal(AwaitMethod.awaitNanos); }
+    public void testSignal_awaitUntil() { testSignal(AwaitMethod.awaitUntil); }
+    public void testSignal(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(acquired.releaseShared(0));
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        sync.release();
+        awaitTermination(t);
+    }
+
+    /**
+     * hasWaiters(null) throws NullPointerException
+     */
+    public void testHasWaitersNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.hasWaiters(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitQueueLength(null) throws NullPointerException
+     */
+    public void testGetWaitQueueLengthNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.getWaitQueueLength(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitingThreads(null) throws NullPointerException
+     */
+    public void testGetWaitingThreadsNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.getWaitingThreads(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalArgumentException if not owned
+     */
+    public void testHasWaitersIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * hasWaiters throws IllegalMonitorStateException if not synced
+     */
+    public void testHasWaitersIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitQueueLengthIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalMonitorStateException if not synced
+     */
+    public void testGetWaitQueueLengthIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitingThreadsIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads throws IllegalMonitorStateException if not synced
+     */
+    public void testGetWaitingThreadsIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * hasWaiters returns true when a thread is waiting, else false
+     */
+    public void testHasWaiters() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertFalse(sync.hasWaiters(c));
+                assertTrue(acquired.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertTrue(sync.hasWaiters(c));
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        assertFalse(sync.hasWaiters(c));
+        sync.release();
+
+        awaitTermination(t);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength returns number of waiting threads
+     */
+    public void testGetWaitQueueLength() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        final Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertEquals(0, sync.getWaitQueueLength(c));
+                assertTrue(acquired1.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+        acquired1.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1);
+        assertEquals(1, sync.getWaitQueueLength(c));
+        sync.release();
+
+        final Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, t1);
+                assertEquals(1, sync.getWaitQueueLength(c));
+                assertTrue(acquired2.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertEquals(2, sync.getWaitQueueLength(c));
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertEquals(0, sync.getWaitQueueLength(c));
+        sync.release();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads returns only and all waiting threads
+     */
+    public void testGetWaitingThreads() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        final Thread t1 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertTrue(sync.getWaitingThreads(c).isEmpty());
+                assertTrue(acquired1.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        final Thread t2 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, t1);
+                assertTrue(sync.getWaitingThreads(c).contains(t1));
+                assertFalse(sync.getWaitingThreads(c).isEmpty());
+                assertEquals(1, sync.getWaitingThreads(c).size());
+                assertTrue(acquired2.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertFalse(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertTrue(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(0, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        t1.start();
+        acquired1.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1);
+        assertTrue(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertFalse(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(1, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        t2.start();
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertTrue(sync.getWaitingThreads(c).contains(t1));
+        assertTrue(sync.getWaitingThreads(c).contains(t2));
+        assertFalse(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(2, sync.getWaitingThreads(c).size());
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertFalse(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertTrue(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(0, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * awaitUninterruptibly is uninterruptible
+     */
+    public void testAwaitUninterruptibly() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch pleaseInterrupt = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                sync.acquire();
+                assertTrue(pleaseInterrupt.releaseShared(0));
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                sync.release();
+            }});
+
+        pleaseInterrupt.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        sync.release();
+        t.interrupt();
+        assertHasWaitersUnlocked(sync, c, t);
+        assertThreadStaysAlive(t);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        sync.release();
+        awaitTermination(t);
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil is interruptible
+     */
+    public void testInterruptible_await()      { testInterruptible(AwaitMethod.await); }
+    public void testInterruptible_awaitTimed() { testInterruptible(AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitNanos() { testInterruptible(AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitUntil() { testInterruptible(AwaitMethod.awaitUntil); }
+    public void testInterruptible(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch pleaseInterrupt = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(pleaseInterrupt.releaseShared(0));
+                await(c, awaitMethod);
+            }});
+
+        pleaseInterrupt.acquireShared(0);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * signalAll wakes up all threads
+     */
+    public void testSignalAll_await()      { testSignalAll(AwaitMethod.await); }
+    public void testSignalAll_awaitTimed() { testSignalAll(AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitNanos() { testSignalAll(AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitUntil() { testSignalAll(AwaitMethod.awaitUntil); }
+    public void testSignalAll(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                acquired1.releaseShared(0);
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                acquired2.releaseShared(0);
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        acquired1.acquireShared(0);
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        sync.release();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * toString indicates current state
+     */
+    public void testToString() {
+        Mutex sync = new Mutex();
+        assertTrue(sync.toString().contains("State = " + Mutex.UNLOCKED));
+        sync.acquire();
+        assertTrue(sync.toString().contains("State = " + Mutex.LOCKED));
+    }
+
+    /**
+     * A serialized AQS deserializes with current state, but no queued threads
+     */
+    public void testSerialization() {
+        Mutex sync = new Mutex();
+        assertFalse(serialClone(sync).isHeldExclusively());
+        sync.acquire();
+        Thread t = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t);
+        assertTrue(sync.isHeldExclusively());
+
+        Mutex clone = serialClone(sync);
+        assertTrue(clone.isHeldExclusively());
+        assertHasExclusiveQueuedThreads(sync, t);
+        assertHasExclusiveQueuedThreads(clone, NO_THREADS);
+        t.interrupt();
+        awaitTermination(t);
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+        assertTrue(clone.isHeldExclusively());
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertHasExclusiveQueuedThreads(clone, NO_THREADS);
+    }
+
+    /**
+     * tryReleaseShared setting state changes getState
+     */
+    public void testGetStateWithReleaseShared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertFalse(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+    }
+
+    /**
+     * releaseShared has no effect when already signalled
+     */
+    public void testReleaseShared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertFalse(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+    }
+
+    /**
+     * acquireSharedInterruptibly returns after release, but not before
+     */
+    public void testAcquireSharedInterruptibly() {
+        final BooleanLatch l = new BooleanLatch();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+                assertTrue(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+                assertTrue(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        assertThreadStaysAlive(t);
+        assertHasSharedQueuedThreads(l, t);
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        awaitTermination(t);
+    }
+
+    /**
+     * tryAcquireSharedNanos returns after release, but not before
+     */
+    public void testTryAcquireSharedNanos() {
+        final BooleanLatch l = new BooleanLatch();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                long nanos = MILLISECONDS.toNanos(2 * LONG_DELAY_MS);
+                assertTrue(l.tryAcquireSharedNanos(0, nanos));
+                assertTrue(l.isSignalled());
+                assertTrue(l.tryAcquireSharedNanos(0, nanos));
+                assertTrue(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        assertThreadStaysAlive(t);
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        awaitTermination(t);
+    }
+
+    /**
+     * acquireSharedInterruptibly is interruptible
+     */
+    public void testAcquireSharedInterruptibly_Interruptible() {
+        final BooleanLatch l = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        t.interrupt();
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+    /**
+     * tryAcquireSharedNanos is interruptible
+     */
+    public void testTryAcquireSharedNanos_Interruptible() {
+        final BooleanLatch l = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                long nanos = MILLISECONDS.toNanos(2 * LONG_DELAY_MS);
+                l.tryAcquireSharedNanos(0, nanos);
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        t.interrupt();
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+    /**
+     * tryAcquireSharedNanos times out if not released before timeout
+     */
+    public void testTryAcquireSharedNanos_Timeout() {
+        final BooleanLatch l = new BooleanLatch();
+        final BooleanLatch observedQueued = new BooleanLatch();
+        final long timeoutMillis = timeoutMillis();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                for (long millis = timeoutMillis();
+                     !observedQueued.isSignalled();
+                     millis *= 2) {
+                    long nanos = MILLISECONDS.toNanos(millis);
+                    long startTime = System.nanoTime();
+                    assertFalse(l.tryAcquireSharedNanos(0, nanos));
+                    assertTrue(millisElapsedSince(startTime) >= millis);
+                }
+                assertFalse(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        observedQueued.releaseShared(0);
+        assertFalse(l.isSignalled());
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ArrayBlockingQueueTest.java b/jsr166-tests/src/test/java/jsr166/ArrayBlockingQueueTest.java
new file mode 100644
index 0000000..ce417ad
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ArrayBlockingQueueTest.java
@@ -0,0 +1,891 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class ArrayBlockingQueueTest extends JSR166TestCase {
+
+    public static class Fair extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new ArrayBlockingQueue(SIZE, true);
+        }
+    }
+
+    public static class NonFair extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new ArrayBlockingQueue(SIZE, false);
+        }
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private ArrayBlockingQueue<Integer> populatedQueue(int n) {
+        ArrayBlockingQueue<Integer> q = new ArrayBlockingQueue<Integer>(n);
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; i++)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * A new queue has the indicated capacity
+     */
+    public void testConstructor1() {
+        assertEquals(SIZE, new ArrayBlockingQueue(SIZE).remainingCapacity());
+    }
+
+    /**
+     * Constructor throws IAE if capacity argument nonpositive
+     */
+    public void testConstructor2() {
+        try {
+            new ArrayBlockingQueue(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new ArrayBlockingQueue(1, true, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            new ArrayBlockingQueue(SIZE, false, elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE-1; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new ArrayBlockingQueue(SIZE, false, Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from too large collection throws IAE
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new ArrayBlockingQueue(SIZE - 1, false, elements);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor7() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE, true, elements);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * Queue transitions from empty to full when elements added
+     */
+    public void testEmptyFull() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(2);
+        assertTrue(q.isEmpty());
+        assertEquals(2, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertFalse(q.offer(three));
+    }
+
+    /**
+     * remainingCapacity decreases on add, increases on remove
+     */
+    public void testRemainingCapacity() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remainingCapacity());
+            assertEquals(SIZE-i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i, q.remainingCapacity());
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * Offer succeeds if not full; fails if full
+     */
+    public void testOffer() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(1);
+        assertTrue(q.offer(zero));
+        assertFalse(q.offer(one));
+    }
+
+    /**
+     * add succeeds if not full; throws ISE if full
+     */
+    public void testAdd() {
+        try {
+            ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+            for (int i = 0; i < SIZE; ++i) {
+                assertTrue(q.add(new Integer(i)));
+            }
+            assertEquals(0, q.remainingCapacity());
+            q.add(new Integer(SIZE));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        try {
+            ArrayBlockingQueue q = populatedQueue(SIZE);
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        try {
+            ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i);
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll throws ISE if not enough room
+     */
+    public void testAddAll4() {
+        try {
+            ArrayBlockingQueue q = new ArrayBlockingQueue(1);
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE; ++i)
+                ints[i] = new Integer(i);
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * Queue contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() throws InterruptedException {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer I = new Integer(i);
+            q.put(I);
+            assertTrue(q.contains(I));
+        }
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly if full
+     */
+    public void testBlockingPut() throws InterruptedException {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i)
+                    q.put(i);
+                assertEquals(SIZE, q.size());
+                assertEquals(0, q.remainingCapacity());
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(SIZE, q.size());
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly waiting for take when full
+     */
+    public void testPutWithTake() throws InterruptedException {
+        final int capacity = 2;
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(capacity);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < capacity; i++)
+                    q.put(i);
+                pleaseTake.countDown();
+                q.put(86);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(0, q.take());
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offer times out if full and elements not taken
+     */
+    public void testTimedOffer() throws InterruptedException {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(2);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Object());
+                q.put(new Object());
+                long startTime = System.nanoTime();
+                assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in FIFO order
+     */
+    public void testTake() throws InterruptedException {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.take());
+        }
+    }
+
+    /**
+     * Take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final ArrayBlockingQueue q = populatedQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.take());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+        checkEmpty(q);
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(i, q.poll(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedQueue(SIZE);
+        final CountDownLatch aboutToWait = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    long t0 = System.nanoTime();
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+                }
+                long t0 = System.nanoTime();
+                aboutToWait.countDown();
+                try {
+                    q.poll(MEDIUM_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {
+                    assertTrue(millisElapsedSince(t0) < MEDIUM_DELAY_MS);
+                }
+            }});
+
+        aboutToWait.await();
+        waitForThreadToEnterWaitState(t, SMALL_DELAY_MS);
+        t.interrupt();
+        awaitTermination(t, MEDIUM_DELAY_MS);
+        checkEmpty(q);
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            assertEquals(i, q.poll());
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertEquals(SIZE, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(one));
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        ArrayBlockingQueue p = new ArrayBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        ArrayBlockingQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE-i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            ArrayBlockingQueue q = populatedQueue(SIZE);
+            ArrayBlockingQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE-i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer I = (Integer)(p.remove());
+                assertFalse(q.contains(I));
+            }
+        }
+    }
+
+    void checkToArray(ArrayBlockingQueue q) {
+        int size = q.size();
+        Object[] o = q.toArray();
+        assertEquals(size, o.length);
+        Iterator it = q.iterator();
+        for (int i = 0; i < size; i++) {
+            Integer x = (Integer) it.next();
+            assertEquals((Integer)o[0] + i, (int) x);
+            assertSame(o[i], x);
+        }
+    }
+
+    /**
+     * toArray() contains all elements in FIFO order
+     */
+    public void testToArray() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray(q);
+            q.add(i);
+        }
+        // Provoke wraparound
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray(q);
+            assertEquals(i, q.poll());
+            checkToArray(q);
+            q.add(SIZE+i);
+        }
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray(q);
+            assertEquals(SIZE+i, q.poll());
+        }
+    }
+
+    void checkToArray2(ArrayBlockingQueue q) {
+        int size = q.size();
+        Integer[] a1 = size == 0 ? null : new Integer[size-1];
+        Integer[] a2 = new Integer[size];
+        Integer[] a3 = new Integer[size+2];
+        if (size > 0) Arrays.fill(a1, 42);
+        Arrays.fill(a2, 42);
+        Arrays.fill(a3, 42);
+        Integer[] b1 = size == 0 ? null : (Integer[]) q.toArray(a1);
+        Integer[] b2 = (Integer[]) q.toArray(a2);
+        Integer[] b3 = (Integer[]) q.toArray(a3);
+        assertSame(a2, b2);
+        assertSame(a3, b3);
+        Iterator it = q.iterator();
+        for (int i = 0; i < size; i++) {
+            Integer x = (Integer) it.next();
+            assertSame(b1[i], x);
+            assertEquals(b1[0] + i, (int) x);
+            assertSame(b2[i], x);
+            assertSame(b3[i], x);
+        }
+        assertNull(a3[size]);
+        assertEquals(42, (int) a3[size+1]);
+        if (size > 0) {
+            assertNotSame(a1, b1);
+            assertEquals(size, b1.length);
+            for (int i = 0; i < a1.length; i++) {
+                assertEquals(42, (int) a1[i]);
+            }
+        }
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray2(q);
+            q.add(i);
+        }
+        // Provoke wraparound
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray2(q);
+            assertEquals(i, q.poll());
+            checkToArray2(q);
+            q.add(SIZE+i);
+        }
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray2(q);
+            assertEquals(SIZE+i, q.poll());
+        }
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() throws InterruptedException {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertEquals(it.next(), q.take());
+        }
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(3);
+        q.add(two);
+        q.add(one);
+        q.add(three);
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertSame(it.next(), one);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+
+        assertEquals("queue should be full", 0, q.remainingCapacity());
+
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+        assertEquals(0, q.size());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * offer transfers elements across Executor tasks
+     */
+    public void testOfferInExecutor() {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(2);
+        q.add(one);
+        q.add(two);
+        ExecutorService executor = Executors.newFixedThreadPool(2);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(q.offer(three));
+                threadsStarted.await();
+                assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(0, q.remainingCapacity());
+            }});
+
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadsStarted.await();
+                assertEquals(0, q.remainingCapacity());
+                assertSame(one, q.take());
+            }});
+
+        joinPool(executor);
+    }
+
+    /**
+     * timed poll retrieves elements across Executor threads
+     */
+    public void testPollInExecutor() {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(2);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        ExecutorService executor = Executors.newFixedThreadPool(2);
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertNull(q.poll());
+                threadsStarted.await();
+                assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                checkEmpty(q);
+            }});
+
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadsStarted.await();
+                q.put(one);
+            }});
+
+        joinPool(executor);
+    }
+
+    /**
+     * A deserialized serialized queue has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * drainTo(c) empties queue into another collection c
+     */
+    public void testDrainTo() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(SIZE, l.size());
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        q.add(zero);
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(zero));
+        assertTrue(q.contains(one));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i)
+            assertEquals(l.get(i), new Integer(i));
+    }
+
+    /**
+     * drainTo empties full queue, unblocking a waiting put.
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final ArrayBlockingQueue q = populatedQueue(SIZE);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Integer(SIZE+1));
+            }});
+
+        t.start();
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        t.join();
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE*2);
+        for (int i = 0; i < SIZE + 2; ++i) {
+            for (int j = 0; j < SIZE; j++)
+                assertTrue(q.offer(new Integer(j)));
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(k, l.size());
+            assertEquals(SIZE-k, q.size());
+            for (int j = 0; j < k; ++j)
+                assertEquals(l.get(j), new Integer(j));
+            while (q.poll() != null) ;
+        }
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ArrayDequeTest.java b/jsr166-tests/src/test/java/jsr166/ArrayDequeTest.java
new file mode 100644
index 0000000..d18a560
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ArrayDequeTest.java
@@ -0,0 +1,888 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.Random;
+
+public class ArrayDequeTest extends JSR166TestCase {
+
+    /**
+     * Returns a new deque of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private ArrayDeque<Integer> populatedDeque(int n) {
+        ArrayDeque<Integer> q = new ArrayDeque<Integer>();
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; ++i)
+            assertTrue(q.offerLast(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * new deque is empty
+     */
+    public void testConstructor1() {
+        assertEquals(0, new ArrayDeque().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            ArrayDeque q = new ArrayDeque((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            Integer[] ints = new Integer[SIZE];
+            ArrayDeque q = new ArrayDeque(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        try {
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i);
+            ArrayDeque q = new ArrayDeque(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Deque contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ArrayDeque q = new ArrayDeque(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        ArrayDeque q = new ArrayDeque();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.removeFirst();
+        q.removeFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i, q.size());
+            q.removeFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * push(null) throws NPE
+     */
+    public void testPushNull() {
+        try {
+            ArrayDeque q = new ArrayDeque(1);
+            q.push(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * peekFirst() returns element inserted with push
+     */
+    public void testPush() {
+        ArrayDeque q = populatedDeque(3);
+        q.pollLast();
+        q.push(four);
+        assertSame(four, q.peekFirst());
+    }
+
+    /**
+     * pop() removes next element, or throws NSEE if empty
+     */
+    public void testPop() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pop());
+        }
+        try {
+            q.pop();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * offer(null) throws NPE
+     */
+    public void testOfferNull() {
+        try {
+            ArrayDeque q = new ArrayDeque();
+            q.offer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offerFirst(null) throws NPE
+     */
+    public void testOfferFirstNull() {
+        try {
+            ArrayDeque q = new ArrayDeque();
+            q.offerFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offerLast(null) throws NPE
+     */
+    public void testOfferLastNull() {
+        try {
+            ArrayDeque q = new ArrayDeque();
+            q.offerLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offer(x) succeeds
+     */
+    public void testOffer() {
+        ArrayDeque q = new ArrayDeque();
+        assertTrue(q.offer(zero));
+        assertTrue(q.offer(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * offerFirst(x) succeeds
+     */
+    public void testOfferFirst() {
+        ArrayDeque q = new ArrayDeque();
+        assertTrue(q.offerFirst(zero));
+        assertTrue(q.offerFirst(one));
+        assertSame(one, q.peekFirst());
+        assertSame(zero, q.peekLast());
+    }
+
+    /**
+     * offerLast(x) succeeds
+     */
+    public void testOfferLast() {
+        ArrayDeque q = new ArrayDeque();
+        assertTrue(q.offerLast(zero));
+        assertTrue(q.offerLast(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        try {
+            ArrayDeque q = new ArrayDeque();
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addFirst(null) throws NPE
+     */
+    public void testAddFirstNull() {
+        try {
+            ArrayDeque q = new ArrayDeque();
+            q.addFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addLast(null) throws NPE
+     */
+    public void testAddLastNull() {
+        try {
+            ArrayDeque q = new ArrayDeque();
+            q.addLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * add(x) succeeds
+     */
+    public void testAdd() {
+        ArrayDeque q = new ArrayDeque();
+        assertTrue(q.add(zero));
+        assertTrue(q.add(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * addFirst(x) succeeds
+     */
+    public void testAddFirst() {
+        ArrayDeque q = new ArrayDeque();
+        q.addFirst(zero);
+        q.addFirst(one);
+        assertSame(one, q.peekFirst());
+        assertSame(zero, q.peekLast());
+    }
+
+    /**
+     * addLast(x) succeeds
+     */
+    public void testAddLast() {
+        ArrayDeque q = new ArrayDeque();
+        q.addLast(zero);
+        q.addLast(one);
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        try {
+            ArrayDeque q = new ArrayDeque();
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        try {
+            ArrayDeque q = new ArrayDeque();
+            Integer[] ints = new Integer[SIZE];
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        try {
+            ArrayDeque q = new ArrayDeque();
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i);
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Deque contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ArrayDeque q = new ArrayDeque();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * pollFirst() succeeds unless empty
+     */
+    public void testPollFirst() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * pollLast() succeeds unless empty
+     */
+    public void testPollLast() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = SIZE-1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollLast());
+    }
+
+    /**
+     * poll() succeeds unless empty
+     */
+    public void testPoll() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * remove() removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i-1));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i+1));
+            assertFalse(q.contains(i+1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * peekFirst() returns next element, or null if empty
+     */
+    public void testPeekFirst() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peekFirst());
+            assertEquals(i, q.pollFirst());
+            assertTrue(q.peekFirst() == null ||
+                       !q.peekFirst().equals(i));
+        }
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * peek() returns next element, or null if empty
+     */
+    public void testPeek() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * peekLast() returns next element, or null if empty
+     */
+    public void testPeekLast() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = SIZE-1; i >= 0; --i) {
+            assertEquals(i, q.peekLast());
+            assertEquals(i, q.pollLast());
+            assertTrue(q.peekLast() == null ||
+                       !q.peekLast().equals(i));
+        }
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * element() returns first element, or throws NSEE if empty
+     */
+    public void testElement() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * getFirst() returns first element, or throws NSEE if empty
+     */
+    public void testFirstElement() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.getFirst());
+            assertEquals(i, q.pollFirst());
+        }
+        try {
+            q.getFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * getLast() returns last element, or throws NSEE if empty
+     */
+    public void testLastElement() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = SIZE-1; i >= 0; --i) {
+            assertEquals(i, q.getLast());
+            assertEquals(i, q.pollLast());
+        }
+        try {
+            q.getLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirst() removes first element, or throws NSEE if empty
+     */
+    public void testRemoveFirst() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.removeFirst());
+        }
+        try {
+            q.removeFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * removeLast() removes last element, or throws NSEE if empty
+     */
+    public void testRemoveLast() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.removeLast());
+        }
+        try {
+            q.removeLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirstOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveFirstOccurrence() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+            assertFalse(q.removeFirstOccurrence(new Integer(i+1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * removeLastOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveLastOccurrence() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+            assertFalse(q.removeLastOccurrence(new Integer(i+1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            assertEquals(i, q.pollFirst());
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        ArrayDeque q = populatedDeque(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertTrue(q.add(new Integer(1)));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        ArrayDeque q = populatedDeque(SIZE);
+        ArrayDeque p = new ArrayDeque();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            assertTrue(p.add(new Integer(i)));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        ArrayDeque q = populatedDeque(SIZE);
+        ArrayDeque p = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            assertEquals(changed, (i > 0));
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE-i, q.size());
+            p.removeFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            ArrayDeque q = populatedDeque(SIZE);
+            ArrayDeque p = populatedDeque(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE-i, q.size());
+            for (int j = 0; j < i; ++j) {
+                assertFalse(q.contains(p.removeFirst()));
+            }
+        }
+    }
+
+    void checkToArray(ArrayDeque q) {
+        int size = q.size();
+        Object[] o = q.toArray();
+        assertEquals(size, o.length);
+        Iterator it = q.iterator();
+        for (int i = 0; i < size; i++) {
+            Integer x = (Integer) it.next();
+            assertEquals((Integer)o[0] + i, (int) x);
+            assertSame(o[i], x);
+        }
+    }
+
+    /**
+     * toArray() contains all elements in FIFO order
+     */
+    public void testToArray() {
+        ArrayDeque q = new ArrayDeque();
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray(q);
+            q.addLast(i);
+        }
+        // Provoke wraparound
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray(q);
+            assertEquals(i, q.poll());
+            q.addLast(SIZE+i);
+        }
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray(q);
+            assertEquals(SIZE+i, q.poll());
+        }
+    }
+
+    void checkToArray2(ArrayDeque q) {
+        int size = q.size();
+        Integer[] a1 = size == 0 ? null : new Integer[size-1];
+        Integer[] a2 = new Integer[size];
+        Integer[] a3 = new Integer[size+2];
+        if (size > 0) Arrays.fill(a1, 42);
+        Arrays.fill(a2, 42);
+        Arrays.fill(a3, 42);
+        Integer[] b1 = size == 0 ? null : (Integer[]) q.toArray(a1);
+        Integer[] b2 = (Integer[]) q.toArray(a2);
+        Integer[] b3 = (Integer[]) q.toArray(a3);
+        assertSame(a2, b2);
+        assertSame(a3, b3);
+        Iterator it = q.iterator();
+        for (int i = 0; i < size; i++) {
+            Integer x = (Integer) it.next();
+            assertSame(b1[i], x);
+            assertEquals(b1[0] + i, (int) x);
+            assertSame(b2[i], x);
+            assertSame(b3[i], x);
+        }
+        assertNull(a3[size]);
+        assertEquals(42, (int) a3[size+1]);
+        if (size > 0) {
+            assertNotSame(a1, b1);
+            assertEquals(size, b1.length);
+            for (int i = 0; i < a1.length; i++) {
+                assertEquals(42, (int) a1[i]);
+            }
+        }
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        ArrayDeque q = new ArrayDeque();
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray2(q);
+            q.addLast(i);
+        }
+        // Provoke wraparound
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray2(q);
+            assertEquals(i, q.poll());
+            q.addLast(SIZE+i);
+        }
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray2(q);
+            assertEquals(SIZE+i, q.poll());
+        }
+    }
+
+    /**
+     * toArray(null) throws NullPointerException
+     */
+    public void testToArray_NullArg() {
+        ArrayDeque l = new ArrayDeque();
+        l.add(new Object());
+        try {
+            l.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        ArrayDeque l = new ArrayDeque();
+        l.add(new Integer(5));
+        try {
+            l.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * Iterator iterates through all elements
+     */
+    public void testIterator() {
+        ArrayDeque q = populatedDeque(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * Iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final ArrayDeque q = new ArrayDeque();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+
+        assertEquals(3, k);
+    }
+
+    /**
+     * iterator.remove() removes current element
+     */
+    public void testIteratorRemove() {
+        final ArrayDeque q = new ArrayDeque();
+        final Random rng = new Random();
+        for (int iters = 0; iters < 100; ++iters) {
+            int max = rng.nextInt(5) + 2;
+            int split = rng.nextInt(max-1) + 1;
+            for (int j = 1; j <= max; ++j)
+                q.add(new Integer(j));
+            Iterator it = q.iterator();
+            for (int j = 1; j <= split; ++j)
+                assertEquals(it.next(), new Integer(j));
+            it.remove();
+            assertEquals(it.next(), new Integer(split+1));
+            for (int j = 1; j <= split; ++j)
+                q.remove(new Integer(j));
+            it = q.iterator();
+            for (int j = split+1; j <= max; ++j) {
+                assertEquals(it.next(), new Integer(j));
+                it.remove();
+            }
+            assertFalse(it.hasNext());
+            assertTrue(q.isEmpty());
+        }
+    }
+
+    /**
+     * Descending iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        ArrayDeque q = populatedDeque(SIZE);
+        int i = 0;
+        Iterator it = q.descendingIterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+        assertFalse(it.hasNext());
+        try {
+            it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * Descending iterator ordering is reverse FIFO
+     */
+    public void testDescendingIteratorOrdering() {
+        final ArrayDeque q = new ArrayDeque();
+        for (int iters = 0; iters < 100; ++iters) {
+            q.add(new Integer(3));
+            q.add(new Integer(2));
+            q.add(new Integer(1));
+            int k = 0;
+            for (Iterator it = q.descendingIterator(); it.hasNext();) {
+                assertEquals(++k, it.next());
+            }
+
+            assertEquals(3, k);
+            q.remove();
+            q.remove();
+            q.remove();
+        }
+    }
+
+    /**
+     * descendingIterator.remove() removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final ArrayDeque q = new ArrayDeque();
+        final Random rng = new Random();
+        for (int iters = 0; iters < 100; ++iters) {
+            int max = rng.nextInt(5) + 2;
+            int split = rng.nextInt(max-1) + 1;
+            for (int j = max; j >= 1; --j)
+                q.add(new Integer(j));
+            Iterator it = q.descendingIterator();
+            for (int j = 1; j <= split; ++j)
+                assertEquals(it.next(), new Integer(j));
+            it.remove();
+            assertEquals(it.next(), new Integer(split+1));
+            for (int j = 1; j <= split; ++j)
+                q.remove(new Integer(j));
+            it = q.descendingIterator();
+            for (int j = split+1; j <= max; ++j) {
+                assertEquals(it.next(), new Integer(j));
+                it.remove();
+            }
+            assertFalse(it.hasNext());
+            assertTrue(q.isEmpty());
+        }
+    }
+
+    /**
+     * toString() contains toStrings of elements
+     */
+    public void testToString() {
+        ArrayDeque q = populatedDeque(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized deque has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedDeque(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicBooleanTest.java b/jsr166-tests/src/test/java/jsr166/AtomicBooleanTest.java
new file mode 100644
index 0000000..7a50120
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/AtomicBooleanTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class AtomicBooleanTest extends JSR166TestCase {
+
+    /**
+     * constructor initializes to given value
+     */
+    public void testConstructor() {
+        assertTrue(new AtomicBoolean(true).get());
+        assertFalse(new AtomicBoolean(false).get());
+    }
+
+    /**
+     * default constructed initializes to false
+     */
+    public void testConstructor2() {
+        AtomicBoolean ai = new AtomicBoolean();
+        assertFalse(ai.get());
+    }
+
+    /**
+     * get returns the last value set
+     */
+    public void testGetSet() {
+        AtomicBoolean ai = new AtomicBoolean(true);
+        assertTrue(ai.get());
+        ai.set(false);
+        assertFalse(ai.get());
+        ai.set(true);
+        assertTrue(ai.get());
+    }
+
+    /**
+     * get returns the last value lazySet in same thread
+     */
+    public void testGetLazySet() {
+        AtomicBoolean ai = new AtomicBoolean(true);
+        assertTrue(ai.get());
+        ai.lazySet(false);
+        assertFalse(ai.get());
+        ai.lazySet(true);
+        assertTrue(ai.get());
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicBoolean ai = new AtomicBoolean(true);
+        assertTrue(ai.compareAndSet(true, false));
+        assertFalse(ai.get());
+        assertTrue(ai.compareAndSet(false, false));
+        assertFalse(ai.get());
+        assertFalse(ai.compareAndSet(true, false));
+        assertFalse(ai.get());
+        assertTrue(ai.compareAndSet(false, true));
+        assertTrue(ai.get());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicBoolean ai = new AtomicBoolean(true);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(false, true)) Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(true, false));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicBoolean ai = new AtomicBoolean(true);
+        while (!ai.weakCompareAndSet(true, false));
+        assertFalse(ai.get());
+        while (!ai.weakCompareAndSet(false, false));
+        assertFalse(ai.get());
+        while (!ai.weakCompareAndSet(false, true));
+        assertTrue(ai.get());
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicBoolean ai = new AtomicBoolean(true);
+        assertEquals(true, ai.getAndSet(false));
+        assertEquals(false, ai.getAndSet(false));
+        assertEquals(false, ai.getAndSet(true));
+        assertTrue(ai.get());
+    }
+
+    /**
+     * a deserialized serialized atomic holds same value
+     */
+    public void testSerialization() throws Exception {
+        AtomicBoolean x = new AtomicBoolean();
+        AtomicBoolean y = serialClone(x);
+        x.set(true);
+        AtomicBoolean z = serialClone(x);
+        assertTrue(x.get());
+        assertFalse(y.get());
+        assertTrue(z.get());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        AtomicBoolean ai = new AtomicBoolean();
+        assertEquals(Boolean.toString(false), ai.toString());
+        ai.set(true);
+        assertEquals(Boolean.toString(true), ai.toString());
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicIntegerArrayTest.java b/jsr166-tests/src/test/java/jsr166/AtomicIntegerArrayTest.java
new file mode 100644
index 0000000..e81a107
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/AtomicIntegerArrayTest.java
@@ -0,0 +1,337 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+
+public class AtomicIntegerArrayTest extends JSR166TestCase {
+
+    /**
+     * constructor creates array of given size with all elements zero
+     */
+    public void testConstructor() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++)
+            assertEquals(0, aa.get(i));
+    }
+
+    /**
+     * constructor with null array throws NPE
+     */
+    public void testConstructor2NPE() {
+        try {
+            int[] a = null;
+            AtomicIntegerArray aa = new AtomicIntegerArray(a);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * constructor with array is of same size and has all elements
+     */
+    public void testConstructor2() {
+        int[] a = { 17, 3, -42, 99, -7 };
+        AtomicIntegerArray aa = new AtomicIntegerArray(a);
+        assertEquals(a.length, aa.length());
+        for (int i = 0; i < a.length; i++)
+            assertEquals(a[i], aa.get(i));
+    }
+
+    /**
+     * get and set for out of bound indices throw IndexOutOfBoundsException
+     */
+    public void testIndexing() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int index : new int[] { -1, SIZE }) {
+            try {
+                aa.get(index);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.set(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.lazySet(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.compareAndSet(index, 1, 2);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.weakCompareAndSet(index, 1, 2);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.getAndAdd(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.addAndGet(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * get returns the last value set at index
+     */
+    public void testGetSet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.get(i));
+            aa.set(i, 2);
+            assertEquals(2, aa.get(i));
+            aa.set(i, -3);
+            assertEquals(-3, aa.get(i));
+        }
+    }
+
+    /**
+     * get returns the last value lazySet at index by same thread
+     */
+    public void testGetLazySet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.lazySet(i, 1);
+            assertEquals(1, aa.get(i));
+            aa.lazySet(i, 2);
+            assertEquals(2, aa.get(i));
+            aa.lazySet(i, -3);
+            assertEquals(-3, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertTrue(aa.compareAndSet(i, 1, 2));
+            assertTrue(aa.compareAndSet(i, 2, -4));
+            assertEquals(-4, aa.get(i));
+            assertFalse(aa.compareAndSet(i, -5, 7));
+            assertEquals(-4, aa.get(i));
+            assertTrue(aa.compareAndSet(i, -4, 7));
+            assertEquals(7, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicIntegerArray a = new AtomicIntegerArray(1);
+        a.set(0, 1);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(0, 2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(0, 1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, a.get(0));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            while (!aa.weakCompareAndSet(i, 1, 2));
+            while (!aa.weakCompareAndSet(i, 2, -4));
+            assertEquals(-4, aa.get(i));
+            while (!aa.weakCompareAndSet(i, -4, 7));
+            assertEquals(7, aa.get(i));
+        }
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value at given index
+     */
+    public void testGetAndSet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndSet(i, 0));
+            assertEquals(0, aa.getAndSet(i, -10));
+            assertEquals(-10, aa.getAndSet(i, 1));
+        }
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndAdd(i, 2));
+            assertEquals(3, aa.get(i));
+            assertEquals(3, aa.getAndAdd(i, -4));
+            assertEquals(-1, aa.get(i));
+        }
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndDecrement(i));
+            assertEquals(0, aa.getAndDecrement(i));
+            assertEquals(-1, aa.getAndDecrement(i));
+        }
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndIncrement(i));
+            assertEquals(2, aa.get(i));
+            aa.set(i, -2);
+            assertEquals(-2, aa.getAndIncrement(i));
+            assertEquals(-1, aa.getAndIncrement(i));
+            assertEquals(0, aa.getAndIncrement(i));
+            assertEquals(1, aa.get(i));
+        }
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(3, aa.addAndGet(i, 2));
+            assertEquals(3, aa.get(i));
+            assertEquals(-1, aa.addAndGet(i, -4));
+            assertEquals(-1, aa.get(i));
+        }
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(0, aa.decrementAndGet(i));
+            assertEquals(-1, aa.decrementAndGet(i));
+            assertEquals(-2, aa.decrementAndGet(i));
+            assertEquals(-2, aa.get(i));
+        }
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(2, aa.incrementAndGet(i));
+            assertEquals(2, aa.get(i));
+            aa.set(i, -2);
+            assertEquals(-1, aa.incrementAndGet(i));
+            assertEquals(0, aa.incrementAndGet(i));
+            assertEquals(1, aa.incrementAndGet(i));
+            assertEquals(1, aa.get(i));
+        }
+    }
+
+    static final int COUNTDOWN = 100000;
+
+    class Counter extends CheckedRunnable {
+        final AtomicIntegerArray aa;
+        volatile int counts;
+        Counter(AtomicIntegerArray a) { aa = a; }
+        public void realRun() {
+            for (;;) {
+                boolean done = true;
+                for (int i = 0; i < aa.length(); i++) {
+                    int v = aa.get(i);
+                    assertTrue(v >= 0);
+                    if (v != 0) {
+                        done = false;
+                        if (aa.compareAndSet(i, v, v-1))
+                            ++counts;
+                    }
+                }
+                if (done)
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Multiple threads using same array of counters successfully
+     * update a number of times equal to total count
+     */
+    public void testCountingInMultipleThreads() throws InterruptedException {
+        final AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++)
+            aa.set(i, COUNTDOWN);
+        Counter c1 = new Counter(aa);
+        Counter c2 = new Counter(aa);
+        Thread t1 = new Thread(c1);
+        Thread t2 = new Thread(c2);
+        t1.start();
+        t2.start();
+        t1.join();
+        t2.join();
+        assertEquals(c1.counts+c2.counts, SIZE * COUNTDOWN);
+    }
+
+    /**
+     * a deserialized serialized array holds same values
+     */
+    public void testSerialization() throws Exception {
+        AtomicIntegerArray x = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++)
+            x.set(i, -i);
+        AtomicIntegerArray y = serialClone(x);
+        assertNotSame(x, y);
+        assertEquals(x.length(), y.length());
+        for (int i = 0; i < SIZE; i++) {
+            assertEquals(x.get(i), y.get(i));
+        }
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        int[] a = { 17, 3, -42, 99, -7 };
+        AtomicIntegerArray aa = new AtomicIntegerArray(a);
+        assertEquals(Arrays.toString(a), aa.toString());
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicIntegerFieldUpdaterTest.java b/jsr166-tests/src/test/java/jsr166/AtomicIntegerFieldUpdaterTest.java
new file mode 100644
index 0000000..f0c1ae6
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/AtomicIntegerFieldUpdaterTest.java
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+
+public class AtomicIntegerFieldUpdaterTest extends JSR166TestCase {
+    volatile int x = 0;
+    int w;
+    long z;
+
+    AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> updaterFor(String fieldName) {
+        return AtomicIntegerFieldUpdater.newUpdater
+            (AtomicIntegerFieldUpdaterTest.class, fieldName);
+    }
+
+    /**
+     * Construction with non-existent field throws RuntimeException
+     */
+    public void testConstructor() {
+        try {
+            updaterFor("y");
+            shouldThrow();
+        } catch (RuntimeException success) {
+            assertNotNull(success.getCause());
+        }
+    }
+
+    /**
+     * construction with field not of given type throws IllegalArgumentException
+     */
+    public void testConstructor2() {
+        try {
+            updaterFor("z");
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * construction with non-volatile field throws IllegalArgumentException
+     */
+    public void testConstructor3() {
+        try {
+            updaterFor("w");
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * get returns the last value set or assigned
+     */
+    public void testGetSet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.get(this));
+        a.set(this, 2);
+        assertEquals(2, a.get(this));
+        a.set(this, -3);
+        assertEquals(-3, a.get(this));
+    }
+
+    /**
+     * get returns the last value lazySet by same thread
+     */
+    public void testGetLazySet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.get(this));
+        a.lazySet(this, 2);
+        assertEquals(2, a.get(this));
+        a.lazySet(this, -3);
+        assertEquals(-3, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertTrue(a.compareAndSet(this, 1, 2));
+        assertTrue(a.compareAndSet(this, 2, -4));
+        assertEquals(-4, a.get(this));
+        assertFalse(a.compareAndSet(this, -5, 7));
+        assertEquals(-4, a.get(this));
+        assertTrue(a.compareAndSet(this, -4, 7));
+        assertEquals(7, a.get(this));
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        x = 1;
+        final AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(AtomicIntegerFieldUpdaterTest.this, 2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(this, 1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, a.get(this));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        while (!a.weakCompareAndSet(this, 1, 2));
+        while (!a.weakCompareAndSet(this, 2, -4));
+        assertEquals(-4, a.get(this));
+        while (!a.weakCompareAndSet(this, -4, 7));
+        assertEquals(7, a.get(this));
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndSet(this, 0));
+        assertEquals(0, a.getAndSet(this, -10));
+        assertEquals(-10, a.getAndSet(this, 1));
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndAdd(this, 2));
+        assertEquals(3, a.get(this));
+        assertEquals(3, a.getAndAdd(this, -4));
+        assertEquals(-1, a.get(this));
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndDecrement(this));
+        assertEquals(0, a.getAndDecrement(this));
+        assertEquals(-1, a.getAndDecrement(this));
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndIncrement(this));
+        assertEquals(2, a.get(this));
+        a.set(this, -2);
+        assertEquals(-2, a.getAndIncrement(this));
+        assertEquals(-1, a.getAndIncrement(this));
+        assertEquals(0, a.getAndIncrement(this));
+        assertEquals(1, a.get(this));
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(3, a.addAndGet(this, 2));
+        assertEquals(3, a.get(this));
+        assertEquals(-1, a.addAndGet(this, -4));
+        assertEquals(-1, a.get(this));
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(0, a.decrementAndGet(this));
+        assertEquals(-1, a.decrementAndGet(this));
+        assertEquals(-2, a.decrementAndGet(this));
+        assertEquals(-2, a.get(this));
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(2, a.incrementAndGet(this));
+        assertEquals(2, a.get(this));
+        a.set(this, -2);
+        assertEquals(-1, a.incrementAndGet(this));
+        assertEquals(0, a.incrementAndGet(this));
+        assertEquals(1, a.incrementAndGet(this));
+        assertEquals(1, a.get(this));
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicIntegerTest.java b/jsr166-tests/src/test/java/jsr166/AtomicIntegerTest.java
new file mode 100644
index 0000000..2afaa73
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/AtomicIntegerTest.java
@@ -0,0 +1,261 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class AtomicIntegerTest extends JSR166TestCase {
+
+    final int[] VALUES = {
+        Integer.MIN_VALUE, -1, 0, 1, 42, Integer.MAX_VALUE,
+    };
+
+    /**
+     * constructor initializes to given value
+     */
+    public void testConstructor() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * default constructed initializes to zero
+     */
+    public void testConstructor2() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals(0, ai.get());
+    }
+
+    /**
+     * get returns the last value set
+     */
+    public void testGetSet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.get());
+        ai.set(2);
+        assertEquals(2, ai.get());
+        ai.set(-3);
+        assertEquals(-3, ai.get());
+    }
+
+    /**
+     * get returns the last value lazySet in same thread
+     */
+    public void testGetLazySet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.get());
+        ai.lazySet(2);
+        assertEquals(2, ai.get());
+        ai.lazySet(-3);
+        assertEquals(-3, ai.get());
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertTrue(ai.compareAndSet(1, 2));
+        assertTrue(ai.compareAndSet(2, -4));
+        assertEquals(-4, ai.get());
+        assertFalse(ai.compareAndSet(-5, 7));
+        assertEquals(-4, ai.get());
+        assertTrue(ai.compareAndSet(-4, 7));
+        assertEquals(7, ai.get());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicInteger ai = new AtomicInteger(1);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, ai.get());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        while (!ai.weakCompareAndSet(1, 2));
+        while (!ai.weakCompareAndSet(2, -4));
+        assertEquals(-4, ai.get());
+        while (!ai.weakCompareAndSet(-4, 7));
+        assertEquals(7, ai.get());
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.getAndSet(0));
+        assertEquals(0, ai.getAndSet(-10));
+        assertEquals(-10, ai.getAndSet(1));
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.getAndAdd(2));
+        assertEquals(3, ai.get());
+        assertEquals(3, ai.getAndAdd(-4));
+        assertEquals(-1, ai.get());
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.getAndDecrement());
+        assertEquals(0, ai.getAndDecrement());
+        assertEquals(-1, ai.getAndDecrement());
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.getAndIncrement());
+        assertEquals(2, ai.get());
+        ai.set(-2);
+        assertEquals(-2, ai.getAndIncrement());
+        assertEquals(-1, ai.getAndIncrement());
+        assertEquals(0, ai.getAndIncrement());
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(3, ai.addAndGet(2));
+        assertEquals(3, ai.get());
+        assertEquals(-1, ai.addAndGet(-4));
+        assertEquals(-1, ai.get());
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(0, ai.decrementAndGet());
+        assertEquals(-1, ai.decrementAndGet());
+        assertEquals(-2, ai.decrementAndGet());
+        assertEquals(-2, ai.get());
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(2, ai.incrementAndGet());
+        assertEquals(2, ai.get());
+        ai.set(-2);
+        assertEquals(-1, ai.incrementAndGet());
+        assertEquals(0, ai.incrementAndGet());
+        assertEquals(1, ai.incrementAndGet());
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * a deserialized serialized atomic holds same value
+     */
+    public void testSerialization() throws Exception {
+        AtomicInteger x = new AtomicInteger();
+        AtomicInteger y = serialClone(x);
+        assertNotSame(x, y);
+        x.set(22);
+        AtomicInteger z = serialClone(x);
+        assertEquals(22, x.get());
+        assertEquals(0, y.get());
+        assertEquals(22, z.get());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals("0", ai.toString());
+        for (int x : VALUES) {
+            ai.set(x);
+            assertEquals(Integer.toString(x), ai.toString());
+        }
+    }
+
+    /**
+     * intValue returns current value.
+     */
+    public void testIntValue() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals(0, ai.intValue());
+        for (int x : VALUES) {
+            ai.set(x);
+            assertEquals(x, ai.intValue());
+        }
+    }
+
+    /**
+     * longValue returns current value.
+     */
+    public void testLongValue() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals(0L, ai.longValue());
+        for (int x : VALUES) {
+            ai.set(x);
+            assertEquals((long)x, ai.longValue());
+        }
+    }
+
+    /**
+     * floatValue returns current value.
+     */
+    public void testFloatValue() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals(0.0f, ai.floatValue());
+        for (int x : VALUES) {
+            ai.set(x);
+            assertEquals((float)x, ai.floatValue());
+        }
+    }
+
+    /**
+     * doubleValue returns current value.
+     */
+    public void testDoubleValue() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals(0.0d, ai.doubleValue());
+        for (int x : VALUES) {
+            ai.set(x);
+            assertEquals((double)x, ai.doubleValue());
+        }
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicLongArrayTest.java b/jsr166-tests/src/test/java/jsr166/AtomicLongArrayTest.java
new file mode 100644
index 0000000..53be5dc
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/AtomicLongArrayTest.java
@@ -0,0 +1,337 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicLongArray;
+
+public class AtomicLongArrayTest extends JSR166TestCase {
+
+    /**
+     * constructor creates array of given size with all elements zero
+     */
+    public void testConstructor() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++)
+            assertEquals(0, aa.get(i));
+    }
+
+    /**
+     * constructor with null array throws NPE
+     */
+    public void testConstructor2NPE() {
+        try {
+            long[] a = null;
+            AtomicLongArray aa = new AtomicLongArray(a);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * constructor with array is of same size and has all elements
+     */
+    public void testConstructor2() {
+        long[] a = { 17L, 3L, -42L, 99L, -7L };
+        AtomicLongArray aa = new AtomicLongArray(a);
+        assertEquals(a.length, aa.length());
+        for (int i = 0; i < a.length; i++)
+            assertEquals(a[i], aa.get(i));
+    }
+
+    /**
+     * get and set for out of bound indices throw IndexOutOfBoundsException
+     */
+    public void testIndexing() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int index : new int[] { -1, SIZE }) {
+            try {
+                aa.get(index);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.set(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.lazySet(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.compareAndSet(index, 1, 2);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.weakCompareAndSet(index, 1, 2);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.getAndAdd(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.addAndGet(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * get returns the last value set at index
+     */
+    public void testGetSet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.get(i));
+            aa.set(i, 2);
+            assertEquals(2, aa.get(i));
+            aa.set(i, -3);
+            assertEquals(-3, aa.get(i));
+        }
+    }
+
+    /**
+     * get returns the last value lazySet at index by same thread
+     */
+    public void testGetLazySet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.lazySet(i, 1);
+            assertEquals(1, aa.get(i));
+            aa.lazySet(i, 2);
+            assertEquals(2, aa.get(i));
+            aa.lazySet(i, -3);
+            assertEquals(-3, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertTrue(aa.compareAndSet(i, 1, 2));
+            assertTrue(aa.compareAndSet(i, 2, -4));
+            assertEquals(-4, aa.get(i));
+            assertFalse(aa.compareAndSet(i, -5, 7));
+            assertEquals(-4, aa.get(i));
+            assertTrue(aa.compareAndSet(i, -4, 7));
+            assertEquals(7, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws InterruptedException {
+        final AtomicLongArray a = new AtomicLongArray(1);
+        a.set(0, 1);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(0, 2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(0, 1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, a.get(0));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            while (!aa.weakCompareAndSet(i, 1, 2));
+            while (!aa.weakCompareAndSet(i, 2, -4));
+            assertEquals(-4, aa.get(i));
+            while (!aa.weakCompareAndSet(i, -4, 7));
+            assertEquals(7, aa.get(i));
+        }
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value at given index
+     */
+    public void testGetAndSet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndSet(i, 0));
+            assertEquals(0, aa.getAndSet(i, -10));
+            assertEquals(-10, aa.getAndSet(i, 1));
+        }
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndAdd(i, 2));
+            assertEquals(3, aa.get(i));
+            assertEquals(3, aa.getAndAdd(i, -4));
+            assertEquals(-1, aa.get(i));
+        }
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndDecrement(i));
+            assertEquals(0, aa.getAndDecrement(i));
+            assertEquals(-1, aa.getAndDecrement(i));
+        }
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndIncrement(i));
+            assertEquals(2, aa.get(i));
+            aa.set(i, -2);
+            assertEquals(-2, aa.getAndIncrement(i));
+            assertEquals(-1, aa.getAndIncrement(i));
+            assertEquals(0, aa.getAndIncrement(i));
+            assertEquals(1, aa.get(i));
+        }
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(3, aa.addAndGet(i, 2));
+            assertEquals(3, aa.get(i));
+            assertEquals(-1, aa.addAndGet(i, -4));
+            assertEquals(-1, aa.get(i));
+        }
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(0, aa.decrementAndGet(i));
+            assertEquals(-1, aa.decrementAndGet(i));
+            assertEquals(-2, aa.decrementAndGet(i));
+            assertEquals(-2, aa.get(i));
+        }
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(2, aa.incrementAndGet(i));
+            assertEquals(2, aa.get(i));
+            aa.set(i, -2);
+            assertEquals(-1, aa.incrementAndGet(i));
+            assertEquals(0, aa.incrementAndGet(i));
+            assertEquals(1, aa.incrementAndGet(i));
+            assertEquals(1, aa.get(i));
+        }
+    }
+
+    static final long COUNTDOWN = 100000;
+
+    class Counter extends CheckedRunnable {
+        final AtomicLongArray aa;
+        volatile long counts;
+        Counter(AtomicLongArray a) { aa = a; }
+        public void realRun() {
+            for (;;) {
+                boolean done = true;
+                for (int i = 0; i < aa.length(); i++) {
+                    long v = aa.get(i);
+                    assertTrue(v >= 0);
+                    if (v != 0) {
+                        done = false;
+                        if (aa.compareAndSet(i, v, v-1))
+                            ++counts;
+                    }
+                }
+                if (done)
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Multiple threads using same array of counters successfully
+     * update a number of times equal to total count
+     */
+    public void testCountingInMultipleThreads() throws InterruptedException {
+        final AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++)
+            aa.set(i, COUNTDOWN);
+        Counter c1 = new Counter(aa);
+        Counter c2 = new Counter(aa);
+        Thread t1 = new Thread(c1);
+        Thread t2 = new Thread(c2);
+        t1.start();
+        t2.start();
+        t1.join();
+        t2.join();
+        assertEquals(c1.counts+c2.counts, SIZE * COUNTDOWN);
+    }
+
+    /**
+     * a deserialized serialized array holds same values
+     */
+    public void testSerialization() throws Exception {
+        AtomicLongArray x = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++)
+            x.set(i, -i);
+        AtomicLongArray y = serialClone(x);
+        assertNotSame(x, y);
+        assertEquals(x.length(), y.length());
+        for (int i = 0; i < SIZE; i++) {
+            assertEquals(x.get(i), y.get(i));
+        }
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        long[] a = { 17, 3, -42, 99, -7 };
+        AtomicLongArray aa = new AtomicLongArray(a);
+        assertEquals(Arrays.toString(a), aa.toString());
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicLongFieldUpdaterTest.java b/jsr166-tests/src/test/java/jsr166/AtomicLongFieldUpdaterTest.java
new file mode 100644
index 0000000..c9374e0
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/AtomicLongFieldUpdaterTest.java
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+
+public class AtomicLongFieldUpdaterTest extends JSR166TestCase {
+    volatile long x = 0;
+    int z;
+    long w;
+
+    AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> updaterFor(String fieldName) {
+        return AtomicLongFieldUpdater.newUpdater
+            (AtomicLongFieldUpdaterTest.class, fieldName);
+    }
+
+    /**
+     * Construction with non-existent field throws RuntimeException
+     */
+    public void testConstructor() {
+        try {
+            updaterFor("y");
+            shouldThrow();
+        } catch (RuntimeException success) {
+            assertNotNull(success.getCause());
+        }
+    }
+
+    /**
+     * construction with field not of given type throws IllegalArgumentException
+     */
+    public void testConstructor2() {
+        try {
+            updaterFor("z");
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * construction with non-volatile field throws IllegalArgumentException
+     */
+    public void testConstructor3() {
+        try {
+            updaterFor("w");
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * get returns the last value set or assigned
+     */
+    public void testGetSet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.get(this));
+        a.set(this, 2);
+        assertEquals(2, a.get(this));
+        a.set(this, -3);
+        assertEquals(-3, a.get(this));
+    }
+
+    /**
+     * get returns the last value lazySet by same thread
+     */
+    public void testGetLazySet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.get(this));
+        a.lazySet(this, 2);
+        assertEquals(2, a.get(this));
+        a.lazySet(this, -3);
+        assertEquals(-3, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertTrue(a.compareAndSet(this, 1, 2));
+        assertTrue(a.compareAndSet(this, 2, -4));
+        assertEquals(-4, a.get(this));
+        assertFalse(a.compareAndSet(this, -5, 7));
+        assertEquals(-4, a.get(this));
+        assertTrue(a.compareAndSet(this, -4, 7));
+        assertEquals(7, a.get(this));
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        x = 1;
+        final AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(AtomicLongFieldUpdaterTest.this, 2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(this, 1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, a.get(this));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        while (!a.weakCompareAndSet(this, 1, 2));
+        while (!a.weakCompareAndSet(this, 2, -4));
+        assertEquals(-4, a.get(this));
+        while (!a.weakCompareAndSet(this, -4, 7));
+        assertEquals(7, a.get(this));
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndSet(this, 0));
+        assertEquals(0, a.getAndSet(this, -10));
+        assertEquals(-10, a.getAndSet(this, 1));
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndAdd(this, 2));
+        assertEquals(3, a.get(this));
+        assertEquals(3, a.getAndAdd(this, -4));
+        assertEquals(-1, a.get(this));
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndDecrement(this));
+        assertEquals(0, a.getAndDecrement(this));
+        assertEquals(-1, a.getAndDecrement(this));
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndIncrement(this));
+        assertEquals(2, a.get(this));
+        a.set(this, -2);
+        assertEquals(-2, a.getAndIncrement(this));
+        assertEquals(-1, a.getAndIncrement(this));
+        assertEquals(0, a.getAndIncrement(this));
+        assertEquals(1, a.get(this));
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(3, a.addAndGet(this, 2));
+        assertEquals(3, a.get(this));
+        assertEquals(-1, a.addAndGet(this, -4));
+        assertEquals(-1, a.get(this));
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(0, a.decrementAndGet(this));
+        assertEquals(-1, a.decrementAndGet(this));
+        assertEquals(-2, a.decrementAndGet(this));
+        assertEquals(-2, a.get(this));
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(2, a.incrementAndGet(this));
+        assertEquals(2, a.get(this));
+        a.set(this, -2);
+        assertEquals(-1, a.incrementAndGet(this));
+        assertEquals(0, a.incrementAndGet(this));
+        assertEquals(1, a.incrementAndGet(this));
+        assertEquals(1, a.get(this));
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicLongTest.java b/jsr166-tests/src/test/java/jsr166/AtomicLongTest.java
new file mode 100644
index 0000000..d300367
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/AtomicLongTest.java
@@ -0,0 +1,264 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.atomic.AtomicLong;
+
+public class AtomicLongTest extends JSR166TestCase {
+
+    final long[] VALUES = {
+        Long.MIN_VALUE,
+        Integer.MIN_VALUE, -1, 0, 1, 42, Integer.MAX_VALUE,
+        Long.MAX_VALUE,
+    };
+
+    /**
+     * constructor initializes to given value
+     */
+    public void testConstructor() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * default constructed initializes to zero
+     */
+    public void testConstructor2() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals(0, ai.get());
+    }
+
+    /**
+     * get returns the last value set
+     */
+    public void testGetSet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.get());
+        ai.set(2);
+        assertEquals(2, ai.get());
+        ai.set(-3);
+        assertEquals(-3, ai.get());
+    }
+
+    /**
+     * get returns the last value lazySet in same thread
+     */
+    public void testGetLazySet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.get());
+        ai.lazySet(2);
+        assertEquals(2, ai.get());
+        ai.lazySet(-3);
+        assertEquals(-3, ai.get());
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertTrue(ai.compareAndSet(1, 2));
+        assertTrue(ai.compareAndSet(2, -4));
+        assertEquals(-4, ai.get());
+        assertFalse(ai.compareAndSet(-5, 7));
+        assertEquals(-4, ai.get());
+        assertTrue(ai.compareAndSet(-4, 7));
+        assertEquals(7, ai.get());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicLong ai = new AtomicLong(1);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, ai.get());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicLong ai = new AtomicLong(1);
+        while (!ai.weakCompareAndSet(1, 2));
+        while (!ai.weakCompareAndSet(2, -4));
+        assertEquals(-4, ai.get());
+        while (!ai.weakCompareAndSet(-4, 7));
+        assertEquals(7, ai.get());
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.getAndSet(0));
+        assertEquals(0, ai.getAndSet(-10));
+        assertEquals(-10, ai.getAndSet(1));
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.getAndAdd(2));
+        assertEquals(3, ai.get());
+        assertEquals(3, ai.getAndAdd(-4));
+        assertEquals(-1, ai.get());
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.getAndDecrement());
+        assertEquals(0, ai.getAndDecrement());
+        assertEquals(-1, ai.getAndDecrement());
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.getAndIncrement());
+        assertEquals(2, ai.get());
+        ai.set(-2);
+        assertEquals(-2, ai.getAndIncrement());
+        assertEquals(-1, ai.getAndIncrement());
+        assertEquals(0, ai.getAndIncrement());
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(3, ai.addAndGet(2));
+        assertEquals(3, ai.get());
+        assertEquals(-1, ai.addAndGet(-4));
+        assertEquals(-1, ai.get());
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(0, ai.decrementAndGet());
+        assertEquals(-1, ai.decrementAndGet());
+        assertEquals(-2, ai.decrementAndGet());
+        assertEquals(-2, ai.get());
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(2, ai.incrementAndGet());
+        assertEquals(2, ai.get());
+        ai.set(-2);
+        assertEquals(-1, ai.incrementAndGet());
+        assertEquals(0, ai.incrementAndGet());
+        assertEquals(1, ai.incrementAndGet());
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * a deserialized serialized atomic holds same value
+     */
+    public void testSerialization() throws Exception {
+        AtomicLong x = new AtomicLong();
+        AtomicLong y = serialClone(x);
+        assertNotSame(x, y);
+        x.set(-22);
+        AtomicLong z = serialClone(x);
+        assertNotSame(y, z);
+        assertEquals(-22, x.get());
+        assertEquals(0, y.get());
+        assertEquals(-22, z.get());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals("0", ai.toString());
+        for (long x : VALUES) {
+            ai.set(x);
+            assertEquals(Long.toString(x), ai.toString());
+        }
+    }
+
+    /**
+     * intValue returns current value.
+     */
+    public void testIntValue() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals(0, ai.intValue());
+        for (long x : VALUES) {
+            ai.set(x);
+            assertEquals((int)x, ai.intValue());
+        }
+    }
+
+    /**
+     * longValue returns current value.
+     */
+    public void testLongValue() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals(0L, ai.longValue());
+        for (long x : VALUES) {
+            ai.set(x);
+            assertEquals(x, ai.longValue());
+        }
+    }
+
+    /**
+     * floatValue returns current value.
+     */
+    public void testFloatValue() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals(0.0f, ai.floatValue());
+        for (long x : VALUES) {
+            ai.set(x);
+            assertEquals((float)x, ai.floatValue());
+        }
+    }
+
+    /**
+     * doubleValue returns current value.
+     */
+    public void testDoubleValue() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals(0.0d, ai.doubleValue());
+        for (long x : VALUES) {
+            ai.set(x);
+            assertEquals((double)x, ai.doubleValue());
+        }
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicMarkableReferenceTest.java b/jsr166-tests/src/test/java/jsr166/AtomicMarkableReferenceTest.java
new file mode 100644
index 0000000..fd1f2f1
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/AtomicMarkableReferenceTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.atomic.AtomicMarkableReference;
+
+public class AtomicMarkableReferenceTest extends JSR166TestCase {
+
+    /**
+     * constructor initializes to given reference and mark
+     */
+    public void testConstructor() {
+        AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        assertSame(one, ai.getReference());
+        assertFalse(ai.isMarked());
+        AtomicMarkableReference a2 = new AtomicMarkableReference(null, true);
+        assertNull(a2.getReference());
+        assertTrue(a2.isMarked());
+    }
+
+    /**
+     * get returns the last values of reference and mark set
+     */
+    public void testGetSet() {
+        boolean[] mark = new boolean[1];
+        AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        assertSame(one, ai.getReference());
+        assertFalse(ai.isMarked());
+        assertSame(one, ai.get(mark));
+        assertFalse(mark[0]);
+        ai.set(two, false);
+        assertSame(two, ai.getReference());
+        assertFalse(ai.isMarked());
+        assertSame(two, ai.get(mark));
+        assertFalse(mark[0]);
+        ai.set(one, true);
+        assertSame(one, ai.getReference());
+        assertTrue(ai.isMarked());
+        assertSame(one, ai.get(mark));
+        assertTrue(mark[0]);
+    }
+
+    /**
+     * attemptMark succeeds in single thread
+     */
+    public void testAttemptMark() {
+        boolean[] mark = new boolean[1];
+        AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        assertFalse(ai.isMarked());
+        assertTrue(ai.attemptMark(one, true));
+        assertTrue(ai.isMarked());
+        assertSame(one, ai.get(mark));
+        assertTrue(mark[0]);
+    }
+
+    /**
+     * compareAndSet succeeds in changing values if equal to expected reference
+     * and mark else fails
+     */
+    public void testCompareAndSet() {
+        boolean[] mark = new boolean[1];
+        AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        assertSame(one, ai.get(mark));
+        assertFalse(ai.isMarked());
+        assertFalse(mark[0]);
+
+        assertTrue(ai.compareAndSet(one, two, false, false));
+        assertSame(two, ai.get(mark));
+        assertFalse(mark[0]);
+
+        assertTrue(ai.compareAndSet(two, m3, false, true));
+        assertSame(m3, ai.get(mark));
+        assertTrue(mark[0]);
+
+        assertFalse(ai.compareAndSet(two, m3, true, true));
+        assertSame(m3, ai.get(mark));
+        assertTrue(mark[0]);
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for reference value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(two, three, false, false))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(one, two, false, false));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(three, ai.getReference());
+        assertFalse(ai.isMarked());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for mark value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads2() throws Exception {
+        final AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(one, one, true, false))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(one, one, false, true));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(one, ai.getReference());
+        assertFalse(ai.isMarked());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing values when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        boolean[] mark = new boolean[1];
+        AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        assertSame(one, ai.get(mark));
+        assertFalse(ai.isMarked());
+        assertFalse(mark[0]);
+
+        while (!ai.weakCompareAndSet(one, two, false, false));
+        assertSame(two, ai.get(mark));
+        assertFalse(mark[0]);
+
+        while (!ai.weakCompareAndSet(two, m3, false, true));
+        assertSame(m3, ai.get(mark));
+        assertTrue(mark[0]);
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicReferenceArrayTest.java b/jsr166-tests/src/test/java/jsr166/AtomicReferenceArrayTest.java
new file mode 100644
index 0000000..0a4f3d9
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/AtomicReferenceArrayTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+
+public class AtomicReferenceArrayTest extends JSR166TestCase {
+
+    /**
+     * constructor creates array of given size with all elements null
+     */
+    public void testConstructor() {
+        AtomicReferenceArray<Integer> aa = new AtomicReferenceArray<Integer>(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            assertNull(aa.get(i));
+        }
+    }
+
+    /**
+     * constructor with null array throws NPE
+     */
+    public void testConstructor2NPE() {
+        try {
+            Integer[] a = null;
+            AtomicReferenceArray<Integer> aa = new AtomicReferenceArray<Integer>(a);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * constructor with array is of same size and has all elements
+     */
+    public void testConstructor2() {
+        Integer[] a = { two, one, three, four, seven };
+        AtomicReferenceArray<Integer> aa = new AtomicReferenceArray<Integer>(a);
+        assertEquals(a.length, aa.length());
+        for (int i = 0; i < a.length; i++)
+            assertEquals(a[i], aa.get(i));
+    }
+
+    /**
+     * Initialize AtomicReferenceArray<Class> with SubClass[]
+     */
+    public void testConstructorSubClassArray() {
+        Integer[] a = { two, one, three, four, seven };
+        AtomicReferenceArray<Number> aa = new AtomicReferenceArray<Number>(a);
+        assertEquals(a.length, aa.length());
+        for (int i = 0; i < a.length; i++) {
+            assertSame(a[i], aa.get(i));
+            Long x = Long.valueOf(i);
+            aa.set(i, x);
+            assertSame(x, aa.get(i));
+        }
+    }
+
+    /**
+     * get and set for out of bound indices throw IndexOutOfBoundsException
+     */
+    public void testIndexing() {
+        AtomicReferenceArray<Integer> aa = new AtomicReferenceArray<Integer>(SIZE);
+        for (int index : new int[] { -1, SIZE }) {
+            try {
+                aa.get(index);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.set(index, null);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.lazySet(index, null);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.compareAndSet(index, null, null);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.weakCompareAndSet(index, null, null);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * get returns the last value set at index
+     */
+    public void testGetSet() {
+        AtomicReferenceArray aa = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, one);
+            assertSame(one, aa.get(i));
+            aa.set(i, two);
+            assertSame(two, aa.get(i));
+            aa.set(i, m3);
+            assertSame(m3, aa.get(i));
+        }
+    }
+
+    /**
+     * get returns the last value lazySet at index by same thread
+     */
+    public void testGetLazySet() {
+        AtomicReferenceArray aa = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.lazySet(i, one);
+            assertSame(one, aa.get(i));
+            aa.lazySet(i, two);
+            assertSame(two, aa.get(i));
+            aa.lazySet(i, m3);
+            assertSame(m3, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicReferenceArray aa = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, one);
+            assertTrue(aa.compareAndSet(i, one, two));
+            assertTrue(aa.compareAndSet(i, two, m4));
+            assertSame(m4, aa.get(i));
+            assertFalse(aa.compareAndSet(i, m5, seven));
+            assertSame(m4, aa.get(i));
+            assertTrue(aa.compareAndSet(i, m4, seven));
+            assertSame(seven, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws InterruptedException {
+        final AtomicReferenceArray a = new AtomicReferenceArray(1);
+        a.set(0, one);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(0, two, three))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(0, one, two));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(three, a.get(0));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicReferenceArray aa = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, one);
+            while (!aa.weakCompareAndSet(i, one, two));
+            while (!aa.weakCompareAndSet(i, two, m4));
+            assertSame(m4, aa.get(i));
+            while (!aa.weakCompareAndSet(i, m4, seven));
+            assertSame(seven, aa.get(i));
+        }
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value at given index
+     */
+    public void testGetAndSet() {
+        AtomicReferenceArray aa = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, one);
+            assertSame(one, aa.getAndSet(i, zero));
+            assertSame(zero, aa.getAndSet(i, m10));
+            assertSame(m10, aa.getAndSet(i, one));
+        }
+    }
+
+    /**
+     * a deserialized serialized array holds same values
+     */
+    public void testSerialization() throws Exception {
+        AtomicReferenceArray x = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            x.set(i, new Integer(-i));
+        }
+        AtomicReferenceArray y = serialClone(x);
+        assertNotSame(x, y);
+        assertEquals(x.length(), y.length());
+        for (int i = 0; i < SIZE; i++) {
+            assertEquals(x.get(i), y.get(i));
+        }
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        Integer[] a = { two, one, three, four, seven };
+        AtomicReferenceArray<Integer> aa = new AtomicReferenceArray<Integer>(a);
+        assertEquals(Arrays.toString(a), aa.toString());
+    }
+}
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicReferenceFieldUpdaterTest.java b/jsr166-tests/src/test/java/jsr166/AtomicReferenceFieldUpdaterTest.java
new file mode 100644
index 0000000..271c7b7
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/AtomicReferenceFieldUpdaterTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+public class AtomicReferenceFieldUpdaterTest extends JSR166TestCase {
+    volatile Integer x = null;
+    Object z;
+    Integer w;
+    volatile int i;
+
+    AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> updaterFor(String fieldName) {
+        return AtomicReferenceFieldUpdater.newUpdater
+            (AtomicReferenceFieldUpdaterTest.class, Integer.class, fieldName);
+    }
+
+    /**
+     * Construction with non-existent field throws RuntimeException
+     */
+    public void testConstructor() {
+        try {
+            updaterFor("y");
+            shouldThrow();
+        } catch (RuntimeException success) {
+            assertNotNull(success.getCause());
+        }
+    }
+
+    /**
+     * construction with field not of given type throws ClassCastException
+     */
+    public void testConstructor2() {
+        try {
+            updaterFor("z");
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * Constructor with non-volatile field throws IllegalArgumentException
+     */
+    public void testConstructor3() {
+        try {
+            updaterFor("w");
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor with non-reference field throws ClassCastException
+     */
+    public void testConstructor4() {
+        try {
+            updaterFor("i");
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * get returns the last value set or assigned
+     */
+    public void testGetSet() {
+        AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer>a;
+        a = updaterFor("x");
+        x = one;
+        assertSame(one, a.get(this));
+        a.set(this, two);
+        assertSame(two, a.get(this));
+        a.set(this, m3);
+        assertSame(m3, a.get(this));
+    }
+
+    /**
+     * get returns the last value lazySet by same thread
+     */
+    public void testGetLazySet() {
+        AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer>a;
+        a = updaterFor("x");
+        x = one;
+        assertSame(one, a.get(this));
+        a.lazySet(this, two);
+        assertSame(two, a.get(this));
+        a.lazySet(this, m3);
+        assertSame(m3, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer>a;
+        a = updaterFor("x");
+        x = one;
+        assertTrue(a.compareAndSet(this, one, two));
+        assertTrue(a.compareAndSet(this, two, m4));
+        assertSame(m4, a.get(this));
+        assertFalse(a.compareAndSet(this, m5, seven));
+        assertFalse(seven == a.get(this));
+        assertTrue(a.compareAndSet(this, m4, seven));
+        assertSame(seven, a.get(this));
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        x = one;
+        final AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer>a;
+        a = updaterFor("x");
+
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(AtomicReferenceFieldUpdaterTest.this, two, three))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(this, one, two));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(three, a.get(this));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer>a;
+        a = updaterFor("x");
+        x = one;
+        while (!a.weakCompareAndSet(this, one, two));
+        while (!a.weakCompareAndSet(this, two, m4));
+        assertSame(m4, a.get(this));
+        while (!a.weakCompareAndSet(this, m4, seven));
+        assertSame(seven, a.get(this));
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer>a;
+        a = updaterFor("x");
+        x = one;
+        assertSame(one, a.getAndSet(this, zero));
+        assertSame(zero, a.getAndSet(this, m10));
+        assertSame(m10, a.getAndSet(this, 1));
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicReferenceTest.java b/jsr166-tests/src/test/java/jsr166/AtomicReferenceTest.java
new file mode 100644
index 0000000..8032546
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/AtomicReferenceTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class AtomicReferenceTest extends JSR166TestCase {
+
+    /**
+     * constructor initializes to given value
+     */
+    public void testConstructor() {
+        AtomicReference ai = new AtomicReference(one);
+        assertSame(one, ai.get());
+    }
+
+    /**
+     * default constructed initializes to null
+     */
+    public void testConstructor2() {
+        AtomicReference ai = new AtomicReference();
+        assertNull(ai.get());
+    }
+
+    /**
+     * get returns the last value set
+     */
+    public void testGetSet() {
+        AtomicReference ai = new AtomicReference(one);
+        assertSame(one, ai.get());
+        ai.set(two);
+        assertSame(two, ai.get());
+        ai.set(m3);
+        assertSame(m3, ai.get());
+    }
+
+    /**
+     * get returns the last value lazySet in same thread
+     */
+    public void testGetLazySet() {
+        AtomicReference ai = new AtomicReference(one);
+        assertSame(one, ai.get());
+        ai.lazySet(two);
+        assertSame(two, ai.get());
+        ai.lazySet(m3);
+        assertSame(m3, ai.get());
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicReference ai = new AtomicReference(one);
+        assertTrue(ai.compareAndSet(one, two));
+        assertTrue(ai.compareAndSet(two, m4));
+        assertSame(m4, ai.get());
+        assertFalse(ai.compareAndSet(m5, seven));
+        assertSame(m4, ai.get());
+        assertTrue(ai.compareAndSet(m4, seven));
+        assertSame(seven, ai.get());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicReference ai = new AtomicReference(one);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(two, three))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(one, two));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(three, ai.get());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicReference ai = new AtomicReference(one);
+        while (!ai.weakCompareAndSet(one, two));
+        while (!ai.weakCompareAndSet(two, m4));
+        assertSame(m4, ai.get());
+        while (!ai.weakCompareAndSet(m4, seven));
+        assertSame(seven, ai.get());
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicReference ai = new AtomicReference(one);
+        assertSame(one, ai.getAndSet(zero));
+        assertSame(zero, ai.getAndSet(m10));
+        assertSame(m10, ai.getAndSet(one));
+    }
+
+    /**
+     * a deserialized serialized atomic holds same value
+     */
+    public void testSerialization() throws Exception {
+        AtomicReference x = new AtomicReference();
+        AtomicReference y = serialClone(x);
+        assertNotSame(x, y);
+        x.set(one);
+        AtomicReference z = serialClone(x);
+        assertNotSame(y, z);
+        assertEquals(one, x.get());
+        assertEquals(null, y.get());
+        assertEquals(one, z.get());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        AtomicReference<Integer> ai = new AtomicReference<Integer>(one);
+        assertEquals(one.toString(), ai.toString());
+        ai.set(two);
+        assertEquals(two.toString(), ai.toString());
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicStampedReferenceTest.java b/jsr166-tests/src/test/java/jsr166/AtomicStampedReferenceTest.java
new file mode 100644
index 0000000..3e6445e
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/AtomicStampedReferenceTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.atomic.AtomicStampedReference;
+
+public class AtomicStampedReferenceTest extends JSR166TestCase {
+
+    /**
+     * constructor initializes to given reference and stamp
+     */
+    public void testConstructor() {
+        AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        assertSame(one, ai.getReference());
+        assertEquals(0, ai.getStamp());
+        AtomicStampedReference a2 = new AtomicStampedReference(null, 1);
+        assertNull(a2.getReference());
+        assertEquals(1, a2.getStamp());
+    }
+
+    /**
+     * get returns the last values of reference and stamp set
+     */
+    public void testGetSet() {
+        int[] mark = new int[1];
+        AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        assertSame(one, ai.getReference());
+        assertEquals(0, ai.getStamp());
+        assertSame(one, ai.get(mark));
+        assertEquals(0, mark[0]);
+        ai.set(two, 0);
+        assertSame(two, ai.getReference());
+        assertEquals(0, ai.getStamp());
+        assertSame(two, ai.get(mark));
+        assertEquals(0, mark[0]);
+        ai.set(one, 1);
+        assertSame(one, ai.getReference());
+        assertEquals(1, ai.getStamp());
+        assertSame(one, ai.get(mark));
+        assertEquals(1, mark[0]);
+    }
+
+    /**
+     * attemptStamp succeeds in single thread
+     */
+    public void testAttemptStamp() {
+        int[] mark = new int[1];
+        AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        assertEquals(0, ai.getStamp());
+        assertTrue(ai.attemptStamp(one, 1));
+        assertEquals(1, ai.getStamp());
+        assertSame(one, ai.get(mark));
+        assertEquals(1, mark[0]);
+    }
+
+    /**
+     * compareAndSet succeeds in changing values if equal to expected reference
+     * and stamp else fails
+     */
+    public void testCompareAndSet() {
+        int[] mark = new int[1];
+        AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        assertSame(one, ai.get(mark));
+        assertEquals(0, ai.getStamp());
+        assertEquals(0, mark[0]);
+
+        assertTrue(ai.compareAndSet(one, two, 0, 0));
+        assertSame(two, ai.get(mark));
+        assertEquals(0, mark[0]);
+
+        assertTrue(ai.compareAndSet(two, m3, 0, 1));
+        assertSame(m3, ai.get(mark));
+        assertEquals(1, mark[0]);
+
+        assertFalse(ai.compareAndSet(two, m3, 1, 1));
+        assertSame(m3, ai.get(mark));
+        assertEquals(1, mark[0]);
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for reference value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(two, three, 0, 0))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(one, two, 0, 0));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(three, ai.getReference());
+        assertEquals(0, ai.getStamp());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for stamp value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads2() throws Exception {
+        final AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(one, one, 1, 2))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(one, one, 0, 1));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(one, ai.getReference());
+        assertEquals(2, ai.getStamp());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing values when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        int[] mark = new int[1];
+        AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        assertSame(one, ai.get(mark));
+        assertEquals(0, ai.getStamp());
+        assertEquals(0, mark[0]);
+
+        while (!ai.weakCompareAndSet(one, two, 0, 0));
+        assertSame(two, ai.get(mark));
+        assertEquals(0, mark[0]);
+
+        while (!ai.weakCompareAndSet(two, m3, 0, 1));
+        assertSame(m3, ai.get(mark));
+        assertEquals(1, mark[0]);
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/BlockingQueueTest.java b/jsr166-tests/src/test/java/jsr166/BlockingQueueTest.java
new file mode 100644
index 0000000..1ed7559
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/BlockingQueueTest.java
@@ -0,0 +1,366 @@
+/*
+ * 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 junit.framework.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+/**
+ * Contains "contract" tests applicable to all BlockingQueue implementations.
+ */
+public abstract class BlockingQueueTest extends JSR166TestCase {
+    /*
+     * This is the start of an attempt to refactor the tests for the
+     * various related implementations of related interfaces without
+     * too much duplicated code.  junit does not really support such
+     * testing.  Here subclasses of TestCase not only contain tests,
+     * but also configuration information that describes the
+     * implementation class, most importantly how to instantiate
+     * instances.
+     */
+
+    //----------------------------------------------------------------
+    // Configuration methods
+    //----------------------------------------------------------------
+
+    /** Returns an empty instance of the implementation class. */
+    protected abstract BlockingQueue emptyCollection();
+
+    /**
+     * Returns an element suitable for insertion in the collection.
+     * Override for collections with unusual element types.
+     */
+    protected Object makeElement(int i) {
+        return Integer.valueOf(i);
+    }
+
+    //----------------------------------------------------------------
+    // Tests
+    //----------------------------------------------------------------
+
+    /**
+     * offer(null) throws NullPointerException
+     */
+    public void testOfferNull() {
+        final Queue q = emptyCollection();
+        try {
+            q.offer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * add(null) throws NullPointerException
+     */
+    public void testAddNull() {
+        final Collection q = emptyCollection();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * timed offer(null) throws NullPointerException
+     */
+    public void testTimedOfferNull() throws InterruptedException {
+        final BlockingQueue q = emptyCollection();
+        long startTime = System.nanoTime();
+        try {
+            q.offer(null, LONG_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+    }
+
+    /**
+     * put(null) throws NullPointerException
+     */
+    public void testPutNull() throws InterruptedException {
+        final BlockingQueue q = emptyCollection();
+        try {
+            q.put(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null) throws NullPointerException
+     */
+    public void testAddAllNull() throws InterruptedException {
+        final Collection q = emptyCollection();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NullPointerException
+     */
+    public void testAddAllNullElements() {
+        final Collection q = emptyCollection();
+        final Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            q.addAll(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * toArray(null) throws NullPointerException
+     */
+    public void testToArray_NullArray() {
+        final Collection q = emptyCollection();
+        try {
+            q.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * drainTo(null) throws NullPointerException
+     */
+    public void testDrainToNull() {
+        final BlockingQueue q = emptyCollection();
+        try {
+            q.drainTo(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * drainTo(this) throws IllegalArgumentException
+     */
+    public void testDrainToSelf() {
+        final BlockingQueue q = emptyCollection();
+        try {
+            q.drainTo(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * drainTo(null, n) throws NullPointerException
+     */
+    public void testDrainToNullN() {
+        final BlockingQueue q = emptyCollection();
+        try {
+            q.drainTo(null, 0);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * drainTo(this, n) throws IllegalArgumentException
+     */
+    public void testDrainToSelfN() {
+        final BlockingQueue q = emptyCollection();
+        try {
+            q.drainTo(q, 0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * drainTo(c, n) returns 0 and does nothing when n <= 0
+     */
+    public void testDrainToNonPositiveMaxElements() {
+        final BlockingQueue q = emptyCollection();
+        final int[] ns = { 0, -1, -42, Integer.MIN_VALUE };
+        for (int n : ns)
+            assertEquals(0, q.drainTo(new ArrayList(), n));
+        if (q.remainingCapacity() > 0) {
+            // Not SynchronousQueue, that is
+            Object one = makeElement(1);
+            q.add(one);
+            ArrayList c = new ArrayList();
+            for (int n : ns)
+                assertEquals(0, q.drainTo(new ArrayList(), n));
+            assertEquals(1, q.size());
+            assertSame(one, q.poll());
+            assertTrue(c.isEmpty());
+        }
+    }
+
+    /**
+     * timed poll before a delayed offer times out; after offer succeeds;
+     * on interruption throws
+     */
+    public void testTimedPollWithOffer() throws InterruptedException {
+        final BlockingQueue q = emptyCollection();
+        final CheckedBarrier barrier = new CheckedBarrier(2);
+        final Object zero = makeElement(0);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+
+                barrier.await();
+
+                assertSame(zero, q.poll(LONG_DELAY_MS, MILLISECONDS));
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                barrier.await();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        barrier.await();
+        long startTime = System.nanoTime();
+        assertTrue(q.offer(zero, LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+
+        barrier.await();
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take() blocks interruptibly when empty
+     */
+    public void testTakeFromEmptyBlocksInterruptibly() {
+        final BlockingQueue q = emptyCollection();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                threadStarted.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(threadStarted);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take() throws InterruptedException immediately if interrupted
+     * before waiting
+     */
+    public void testTakeFromEmptyAfterInterrupt() {
+        final BlockingQueue q = emptyCollection();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * timed poll() blocks interruptibly when empty
+     */
+    public void testTimedPollFromEmptyBlocksInterruptibly() {
+        final BlockingQueue q = emptyCollection();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                threadStarted.countDown();
+                try {
+                    q.poll(2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(threadStarted);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed poll() throws InterruptedException immediately if
+     * interrupted before waiting
+     */
+    public void testTimedPollFromEmptyAfterInterrupt() {
+        final BlockingQueue q = emptyCollection();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                try {
+                    q.poll(2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     * TODO: move to superclass CollectionTest.java
+     */
+    public void testRemoveElement() {
+        final BlockingQueue q = emptyCollection();
+        final int size = Math.min(q.remainingCapacity(), SIZE);
+        final Object[] elts = new Object[size];
+        assertFalse(q.contains(makeElement(99)));
+        assertFalse(q.remove(makeElement(99)));
+        checkEmpty(q);
+        for (int i = 0; i < size; i++)
+            q.add(elts[i] = makeElement(i));
+        for (int i = 1; i < size; i+=2) {
+            for (int pass = 0; pass < 2; pass++) {
+                assertEquals((pass == 0), q.contains(elts[i]));
+                assertEquals((pass == 0), q.remove(elts[i]));
+                assertFalse(q.contains(elts[i]));
+                assertTrue(q.contains(elts[i-1]));
+                if (i < size - 1)
+                    assertTrue(q.contains(elts[i+1]));
+            }
+        }
+        if (size > 0)
+            assertTrue(q.contains(elts[0]));
+        for (int i = size-2; i >= 0; i-=2) {
+            assertTrue(q.contains(elts[i]));
+            assertFalse(q.contains(elts[i+1]));
+            assertTrue(q.remove(elts[i]));
+            assertFalse(q.contains(elts[i]));
+            assertFalse(q.remove(elts[i+1]));
+            assertFalse(q.contains(elts[i+1]));
+        }
+        checkEmpty(q);
+    }
+
+    /** For debugging. */
+    public void XXXXtestFails() {
+        fail(emptyCollection().getClass().toString());
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ConcurrentHashMapTest.java b/jsr166-tests/src/test/java/jsr166/ConcurrentHashMapTest.java
new file mode 100644
index 0000000..5d9e2ac
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ConcurrentHashMapTest.java
@@ -0,0 +1,686 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class ConcurrentHashMapTest extends JSR166TestCase {
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static ConcurrentHashMap map5() {
+        ConcurrentHashMap map = new ConcurrentHashMap(5);
+        assertTrue(map.isEmpty());
+        map.put(one, "A");
+        map.put(two, "B");
+        map.put(three, "C");
+        map.put(four, "D");
+        map.put(five, "E");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map;
+    }
+
+    // classes for testing Comparable fallbacks
+    static class BI implements Comparable<BI> {
+        private final int value;
+        BI(int value) { this.value = value; }
+        public int compareTo(BI other) {
+            return Integer.compare(value, other.value);
+        }
+        public boolean equals(Object x) {
+            return (x instanceof BI) && ((BI)x).value == value;
+        }
+        public int hashCode() { return 42; }
+    }
+    static class CI extends BI { CI(int value) { super(value); } }
+    static class DI extends BI { DI(int value) { super(value); } }
+
+    static class BS implements Comparable<BS> {
+        private final String value;
+        BS(String value) { this.value = value; }
+        public int compareTo(BS other) {
+            return value.compareTo(other.value);
+        }
+        public boolean equals(Object x) {
+            return (x instanceof BS) && value.equals(((BS)x).value);
+        }
+        public int hashCode() { return 42; }
+    }
+
+    static class LexicographicList<E extends Comparable<E>> extends ArrayList<E>
+        implements Comparable<LexicographicList<E>> {
+        static long total;
+        static long n;
+        LexicographicList(Collection<E> c) { super(c); }
+        LexicographicList(E e) { super(Collections.singleton(e)); }
+        public int compareTo(LexicographicList<E> other) {
+            long start = System.currentTimeMillis();
+            int common = Math.min(size(), other.size());
+            int r = 0;
+            for (int i = 0; i < common; i++) {
+                if ((r = get(i).compareTo(other.get(i))) != 0)
+                    break;
+            }
+            if (r == 0)
+                r = Integer.compare(size(), other.size());
+            total += System.currentTimeMillis() - start;
+            n++;
+            return r;
+        }
+        private static final long serialVersionUID = 0;
+    }
+
+    /**
+     * Inserted elements that are subclasses of the same Comparable
+     * class are found.
+     */
+    public void testComparableFamily() {
+        ConcurrentHashMap<BI, Boolean> m =
+            new ConcurrentHashMap<BI, Boolean>();
+        for (int i = 0; i < 1000; i++) {
+            assertTrue(m.put(new CI(i), true) == null);
+        }
+        for (int i = 0; i < 1000; i++) {
+            assertTrue(m.containsKey(new CI(i)));
+            assertTrue(m.containsKey(new DI(i)));
+        }
+    }
+
+    /**
+     * Elements of classes with erased generic type parameters based
+     * on Comparable can be inserted and found.
+     */
+     public void testGenericComparable() {
+         ConcurrentHashMap<Object, Boolean> m =
+             new ConcurrentHashMap<Object, Boolean>();
+         for (int i = 0; i < 1000; i++) {
+             BI bi = new BI(i);
+             BS bs = new BS(String.valueOf(i));
+             LexicographicList<BI> bis = new LexicographicList<BI>(bi);
+             LexicographicList<BS> bss = new LexicographicList<BS>(bs);
+             assertTrue(m.putIfAbsent(bis, true) == null);
+             assertTrue(m.containsKey(bis));
+             if (m.putIfAbsent(bss, true) == null)
+                 assertTrue(m.containsKey(bss));
+             assertTrue(m.containsKey(bis));
+         }
+         for (int i = 0; i < 1000; i++) {
+             assertTrue(m.containsKey(new ArrayList(Collections.singleton(new BI(i)))));
+         }
+     }
+
+    /**
+     * Elements of non-comparable classes equal to those of classes
+     * with erased generic type parameters based on Comparable can be
+     * inserted and found.
+     */
+    public void testGenericComparable2() {
+        ConcurrentHashMap<Object, Boolean> m =
+            new ConcurrentHashMap<Object, Boolean>();
+        for (int i = 0; i < 1000; i++) {
+            m.put(new ArrayList(Collections.singleton(new BI(i))), true);
+        }
+
+        for (int i = 0; i < 1000; i++) {
+            LexicographicList<BI> bis = new LexicographicList<BI>(new BI(i));
+            assertTrue(m.containsKey(bis));
+        }
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testClear() {
+        ConcurrentHashMap map = map5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testEquals() {
+        ConcurrentHashMap map1 = map5();
+        ConcurrentHashMap map2 = map5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * contains returns true for contained value
+     */
+    public void testContains() {
+        ConcurrentHashMap map = map5();
+        assertTrue(map.contains("A"));
+        assertFalse(map.contains("Z"));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testContainsKey() {
+        ConcurrentHashMap map = map5();
+        assertTrue(map.containsKey(one));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testContainsValue() {
+        ConcurrentHashMap map = map5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * enumeration returns an enumeration containing the correct
+     * elements
+     */
+    public void testEnumeration() {
+        ConcurrentHashMap map = map5();
+        Enumeration e = map.elements();
+        int count = 0;
+        while (e.hasMoreElements()) {
+            count++;
+            e.nextElement();
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testGet() {
+        ConcurrentHashMap map = map5();
+        assertEquals("A", (String)map.get(one));
+        ConcurrentHashMap empty = new ConcurrentHashMap();
+        assertNull(map.get("anything"));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testIsEmpty() {
+        ConcurrentHashMap empty = new ConcurrentHashMap();
+        ConcurrentHashMap map = map5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * keys returns an enumeration containing all the keys from the map
+     */
+    public void testKeys() {
+        ConcurrentHashMap map = map5();
+        Enumeration e = map.keys();
+        int count = 0;
+        while (e.hasMoreElements()) {
+            count++;
+            e.nextElement();
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testKeySet() {
+        ConcurrentHashMap map = map5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(one));
+        assertTrue(s.contains(two));
+        assertTrue(s.contains(three));
+        assertTrue(s.contains(four));
+        assertTrue(s.contains(five));
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testKeySetToArray() {
+        ConcurrentHashMap map = map5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * Values.toArray contains all values
+     */
+    public void testValuesToArray() {
+        ConcurrentHashMap map = map5();
+        Collection v = map.values();
+        Object[] ar = v.toArray();
+        ArrayList s = new ArrayList(Arrays.asList(ar));
+        assertEquals(5, ar.length);
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet.toArray contains all entries
+     */
+    public void testEntrySetToArray() {
+        ConcurrentHashMap map = map5();
+        Set s = map.entrySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        for (int i = 0; i < 5; ++i) {
+            assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
+            assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
+        }
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testValues() {
+        ConcurrentHashMap map = map5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testEntrySet() {
+        ConcurrentHashMap map = map5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testPutAll() {
+        ConcurrentHashMap empty = new ConcurrentHashMap();
+        ConcurrentHashMap map = map5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(one));
+        assertTrue(empty.containsKey(two));
+        assertTrue(empty.containsKey(three));
+        assertTrue(empty.containsKey(four));
+        assertTrue(empty.containsKey(five));
+    }
+
+    /**
+     * putIfAbsent works when the given key is not present
+     */
+    public void testPutIfAbsent() {
+        ConcurrentHashMap map = map5();
+        map.putIfAbsent(six, "Z");
+        assertTrue(map.containsKey(six));
+    }
+
+    /**
+     * putIfAbsent does not add the pair if the key is already present
+     */
+    public void testPutIfAbsent2() {
+        ConcurrentHashMap map = map5();
+        assertEquals("A", map.putIfAbsent(one, "Z"));
+    }
+
+    /**
+     * replace fails when the given key is not present
+     */
+    public void testReplace() {
+        ConcurrentHashMap map = map5();
+        assertNull(map.replace(six, "Z"));
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * replace succeeds if the key is already present
+     */
+    public void testReplace2() {
+        ConcurrentHashMap map = map5();
+        assertNotNull(map.replace(one, "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * replace value fails when the given key not mapped to expected value
+     */
+    public void testReplaceValue() {
+        ConcurrentHashMap map = map5();
+        assertEquals("A", map.get(one));
+        assertFalse(map.replace(one, "Z", "Z"));
+        assertEquals("A", map.get(one));
+    }
+
+    /**
+     * replace value succeeds when the given key mapped to expected value
+     */
+    public void testReplaceValue2() {
+        ConcurrentHashMap map = map5();
+        assertEquals("A", map.get(one));
+        assertTrue(map.replace(one, "A", "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testRemove() {
+        ConcurrentHashMap map = map5();
+        map.remove(five);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+    }
+
+    /**
+     * remove(key,value) removes only if pair present
+     */
+    public void testRemove2() {
+        ConcurrentHashMap map = map5();
+        map.remove(five, "E");
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+        map.remove(four, "A");
+        assertEquals(4, map.size());
+        assertTrue(map.containsKey(four));
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testSize() {
+        ConcurrentHashMap map = map5();
+        ConcurrentHashMap empty = new ConcurrentHashMap();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        ConcurrentHashMap map = map5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception tests
+
+    /**
+     * Cannot create with negative capacity
+     */
+    public void testConstructor1() {
+        try {
+            new ConcurrentHashMap(-1,0,1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Cannot create with negative concurrency level
+     */
+    public void testConstructor2() {
+        try {
+            new ConcurrentHashMap(1,0,-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Cannot create with only negative capacity
+     */
+    public void testConstructor3() {
+        try {
+            new ConcurrentHashMap(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * get(null) throws NPE
+     */
+    public void testGet_NullPointerException() {
+        try {
+            ConcurrentHashMap c = new ConcurrentHashMap(5);
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) throws NPE
+     */
+    public void testContainsKey_NullPointerException() {
+        try {
+            ConcurrentHashMap c = new ConcurrentHashMap(5);
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsValue(null) throws NPE
+     */
+    public void testContainsValue_NullPointerException() {
+        try {
+            ConcurrentHashMap c = new ConcurrentHashMap(5);
+            c.containsValue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * contains(null) throws NPE
+     */
+    public void testContains_NullPointerException() {
+        try {
+            ConcurrentHashMap c = new ConcurrentHashMap(5);
+            c.contains(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testPut1_NullPointerException() {
+        try {
+            ConcurrentHashMap c = new ConcurrentHashMap(5);
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(x, null) throws NPE
+     */
+    public void testPut2_NullPointerException() {
+        try {
+            ConcurrentHashMap c = new ConcurrentHashMap(5);
+            c.put("whatever", null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * putIfAbsent(null, x) throws NPE
+     */
+    public void testPutIfAbsent1_NullPointerException() {
+        try {
+            ConcurrentHashMap c = new ConcurrentHashMap(5);
+            c.putIfAbsent(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x) throws NPE
+     */
+    public void testReplace_NullPointerException() {
+        try {
+            ConcurrentHashMap c = new ConcurrentHashMap(5);
+            c.replace(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x, y) throws NPE
+     */
+    public void testReplaceValue_NullPointerException() {
+        try {
+            ConcurrentHashMap c = new ConcurrentHashMap(5);
+            c.replace(null, one, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * putIfAbsent(x, null) throws NPE
+     */
+    public void testPutIfAbsent2_NullPointerException() {
+        try {
+            ConcurrentHashMap c = new ConcurrentHashMap(5);
+            c.putIfAbsent("whatever", null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(x, null) throws NPE
+     */
+    public void testReplace2_NullPointerException() {
+        try {
+            ConcurrentHashMap c = new ConcurrentHashMap(5);
+            c.replace("whatever", null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(x, null, y) throws NPE
+     */
+    public void testReplaceValue2_NullPointerException() {
+        try {
+            ConcurrentHashMap c = new ConcurrentHashMap(5);
+            c.replace("whatever", null, "A");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(x, y, null) throws NPE
+     */
+    public void testReplaceValue3_NullPointerException() {
+        try {
+            ConcurrentHashMap c = new ConcurrentHashMap(5);
+            c.replace("whatever", one, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE
+     */
+    public void testRemove1_NullPointerException() {
+        try {
+            ConcurrentHashMap c = new ConcurrentHashMap(5);
+            c.put("sadsdf", "asdads");
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null, x) throws NPE
+     */
+    public void testRemove2_NullPointerException() {
+        try {
+            ConcurrentHashMap c = new ConcurrentHashMap(5);
+            c.put("sadsdf", "asdads");
+            c.remove(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(x, null) returns false
+     */
+    public void testRemove3() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        c.put("sadsdf", "asdads");
+        assertFalse(c.remove("sadsdf", null));
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testSerialization() throws Exception {
+        Map x = map5();
+        Map y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * SetValue of an EntrySet entry sets value in the map.
+     */
+    public void testSetValueWriteThrough() {
+        // Adapted from a bug report by Eric Zoerner
+        ConcurrentHashMap map = new ConcurrentHashMap(2, 5.0f, 1);
+        assertTrue(map.isEmpty());
+        for (int i = 0; i < 20; i++)
+            map.put(new Integer(i), new Integer(i));
+        assertFalse(map.isEmpty());
+        Map.Entry entry1 = (Map.Entry)map.entrySet().iterator().next();
+        // Unless it happens to be first (in which case remainder of
+        // test is skipped), remove a possibly-colliding key from map
+        // which, under some implementations, may cause entry1 to be
+        // cloned in map
+        if (!entry1.getKey().equals(new Integer(16))) {
+            map.remove(new Integer(16));
+            entry1.setValue("XYZ");
+            assertTrue(map.containsValue("XYZ")); // fails if write-through broken
+        }
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ConcurrentLinkedDequeTest.java b/jsr166-tests/src/test/java/jsr166/ConcurrentLinkedDequeTest.java
new file mode 100644
index 0000000..f5b8318
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ConcurrentLinkedDequeTest.java
@@ -0,0 +1,858 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.Random;
+import java.util.concurrent.ConcurrentLinkedDeque;
+
+public class ConcurrentLinkedDequeTest extends JSR166TestCase {
+
+    /**
+     * Returns a new deque of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private ConcurrentLinkedDeque<Integer> populatedDeque(int n) {
+        ConcurrentLinkedDeque<Integer> q = new ConcurrentLinkedDeque<Integer>();
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; ++i)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * new deque is empty
+     */
+    public void testConstructor1() {
+        assertTrue(new ConcurrentLinkedDeque().isEmpty());
+        assertEquals(0, new ConcurrentLinkedDeque().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            ConcurrentLinkedDeque q = new ConcurrentLinkedDeque((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            Integer[] ints = new Integer[SIZE];
+            ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        try {
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i);
+            ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Deque contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size() changes when elements added and removed
+     */
+    public void testSize() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * push(null) throws NPE
+     */
+    public void testPushNull() {
+        try {
+            ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+            q.push(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * peekFirst() returns element inserted with push
+     */
+    public void testPush() {
+        ConcurrentLinkedDeque q = populatedDeque(3);
+        q.pollLast();
+        q.push(four);
+        assertSame(four, q.peekFirst());
+    }
+
+    /**
+     * pop() removes first element, or throws NSEE if empty
+     */
+    public void testPop() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pop());
+        }
+        try {
+            q.pop();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * offer(null) throws NPE
+     */
+    public void testOfferNull() {
+        try {
+            ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+            q.offer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offerFirst(null) throws NPE
+     */
+    public void testOfferFirstNull() {
+        try {
+            ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+            q.offerFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offerLast(null) throws NPE
+     */
+    public void testOfferLastNull() {
+        try {
+            ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+            q.offerLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offer(x) succeeds
+     */
+    public void testOffer() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertTrue(q.offer(zero));
+        assertTrue(q.offer(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * offerFirst(x) succeeds
+     */
+    public void testOfferFirst() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertTrue(q.offerFirst(zero));
+        assertTrue(q.offerFirst(one));
+        assertSame(one, q.peekFirst());
+        assertSame(zero, q.peekLast());
+    }
+
+    /**
+     * offerLast(x) succeeds
+     */
+    public void testOfferLast() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertTrue(q.offerLast(zero));
+        assertTrue(q.offerLast(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        try {
+            ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addFirst(null) throws NPE
+     */
+    public void testAddFirstNull() {
+        try {
+            ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+            q.addFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addLast(null) throws NPE
+     */
+    public void testAddLastNull() {
+        try {
+            ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+            q.addLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * add(x) succeeds
+     */
+    public void testAdd() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertTrue(q.add(zero));
+        assertTrue(q.add(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * addFirst(x) succeeds
+     */
+    public void testAddFirst() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        q.addFirst(zero);
+        q.addFirst(one);
+        assertSame(one, q.peekFirst());
+        assertSame(zero, q.peekLast());
+    }
+
+    /**
+     * addLast(x) succeeds
+     */
+    public void testAddLast() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        q.addLast(zero);
+        q.addLast(one);
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        try {
+            ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        try {
+            ConcurrentLinkedDeque q = populatedDeque(SIZE);
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        try {
+            ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+            Integer[] ints = new Integer[SIZE];
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        try {
+            ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i);
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Deque contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * pollFirst() succeeds unless empty
+     */
+    public void testPollFirst() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * pollLast() succeeds unless empty
+     */
+    public void testPollLast() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = SIZE-1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollLast());
+    }
+
+    /**
+     * poll() succeeds unless empty
+     */
+    public void testPoll() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * peek() returns next element, or null if empty
+     */
+    public void testPeek() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element() returns first element, or throws NSEE if empty
+     */
+    public void testElement() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove() removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i-1));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i+1));
+            assertFalse(q.contains(i+1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * peekFirst() returns next element, or null if empty
+     */
+    public void testPeekFirst() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peekFirst());
+            assertEquals(i, q.pollFirst());
+            assertTrue(q.peekFirst() == null ||
+                       !q.peekFirst().equals(i));
+        }
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * peekLast() returns next element, or null if empty
+     */
+    public void testPeekLast() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = SIZE-1; i >= 0; --i) {
+            assertEquals(i, q.peekLast());
+            assertEquals(i, q.pollLast());
+            assertTrue(q.peekLast() == null ||
+                       !q.peekLast().equals(i));
+        }
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * getFirst() returns first element, or throws NSEE if empty
+     */
+    public void testFirstElement() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.getFirst());
+            assertEquals(i, q.pollFirst());
+        }
+        try {
+            q.getFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * getLast() returns last element, or throws NSEE if empty
+     */
+    public void testLastElement() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = SIZE-1; i >= 0; --i) {
+            assertEquals(i, q.getLast());
+            assertEquals(i, q.pollLast());
+        }
+        try {
+            q.getLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirst() removes first element, or throws NSEE if empty
+     */
+    public void testRemoveFirst() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.removeFirst());
+        }
+        try {
+            q.removeFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * removeLast() removes last element, or throws NSEE if empty
+     */
+    public void testRemoveLast() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.removeLast());
+        }
+        try {
+            q.removeLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirstOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveFirstOccurrence() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+            assertFalse(q.removeFirstOccurrence(new Integer(i+1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * removeLastOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveLastOccurrence() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+            assertFalse(q.removeLastOccurrence(new Integer(i+1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear() removes all elements
+     */
+    public void testClear() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        ConcurrentLinkedDeque p = new ConcurrentLinkedDeque();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if change
+     */
+    public void testRetainAll() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        ConcurrentLinkedDeque p = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE-i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            ConcurrentLinkedDeque q = populatedDeque(SIZE);
+            ConcurrentLinkedDeque p = populatedDeque(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE-i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer I = (Integer)(p.remove());
+                assertFalse(q.contains(I));
+            }
+        }
+    }
+
+    /**
+     * toArray() contains all elements in FIFO order
+     */
+    public void testToArray() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        ConcurrentLinkedDeque<Integer> q = populatedDeque(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.poll());
+    }
+
+    /**
+     * toArray(null) throws NullPointerException
+     */
+    public void testToArray_NullArg() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        try {
+            q.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * Iterator iterates through all elements
+     */
+    public void testIterator() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * Iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+
+        assertEquals("deque should be empty again", 0, q.size());
+    }
+
+    /**
+     * iterator.remove() removes current element
+     */
+    public void testIteratorRemove() {
+        final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        final Random rng = new Random();
+        for (int iters = 0; iters < 100; ++iters) {
+            int max = rng.nextInt(5) + 2;
+            int split = rng.nextInt(max-1) + 1;
+            for (int j = 1; j <= max; ++j)
+                q.add(new Integer(j));
+            Iterator it = q.iterator();
+            for (int j = 1; j <= split; ++j)
+                assertEquals(it.next(), new Integer(j));
+            it.remove();
+            assertEquals(it.next(), new Integer(split+1));
+            for (int j = 1; j <= split; ++j)
+                q.remove(new Integer(j));
+            it = q.iterator();
+            for (int j = split+1; j <= max; ++j) {
+                assertEquals(it.next(), new Integer(j));
+                it.remove();
+            }
+            assertFalse(it.hasNext());
+            assertTrue(q.isEmpty());
+        }
+    }
+
+    /**
+     * Descending iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        int i = 0;
+        Iterator it = q.descendingIterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+        assertFalse(it.hasNext());
+        try {
+            it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * Descending iterator ordering is reverse FIFO
+     */
+    public void testDescendingIteratorOrdering() {
+        final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        for (int iters = 0; iters < 100; ++iters) {
+            q.add(new Integer(3));
+            q.add(new Integer(2));
+            q.add(new Integer(1));
+            int k = 0;
+            for (Iterator it = q.descendingIterator(); it.hasNext();) {
+                assertEquals(++k, it.next());
+            }
+
+            assertEquals(3, k);
+            q.remove();
+            q.remove();
+            q.remove();
+        }
+    }
+
+    /**
+     * descendingIterator.remove() removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        final Random rng = new Random();
+        for (int iters = 0; iters < 100; ++iters) {
+            int max = rng.nextInt(5) + 2;
+            int split = rng.nextInt(max-1) + 1;
+            for (int j = max; j >= 1; --j)
+                q.add(new Integer(j));
+            Iterator it = q.descendingIterator();
+            for (int j = 1; j <= split; ++j)
+                assertEquals(it.next(), new Integer(j));
+            it.remove();
+            assertEquals(it.next(), new Integer(split+1));
+            for (int j = 1; j <= split; ++j)
+                q.remove(new Integer(j));
+            it = q.descendingIterator();
+            for (int j = split+1; j <= max; ++j) {
+                assertEquals(it.next(), new Integer(j));
+                it.remove();
+            }
+            assertFalse(it.hasNext());
+            assertTrue(q.isEmpty());
+        }
+    }
+
+    /**
+     * toString() contains toStrings of elements
+     */
+    public void testToString() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized deque has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedDeque(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ConcurrentLinkedQueueTest.java b/jsr166-tests/src/test/java/jsr166/ConcurrentLinkedQueueTest.java
new file mode 100644
index 0000000..7924034
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ConcurrentLinkedQueueTest.java
@@ -0,0 +1,511 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+public class ConcurrentLinkedQueueTest extends JSR166TestCase {
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private ConcurrentLinkedQueue<Integer> populatedQueue(int n) {
+        ConcurrentLinkedQueue<Integer> q = new ConcurrentLinkedQueue<Integer>();
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; ++i)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * new queue is empty
+     */
+    public void testConstructor1() {
+        assertEquals(0, new ConcurrentLinkedQueue().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            ConcurrentLinkedQueue q = new ConcurrentLinkedQueue((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            Integer[] ints = new Integer[SIZE];
+            ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        try {
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i);
+            ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * offer(null) throws NPE
+     */
+    public void testOfferNull() {
+        try {
+            ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+            q.offer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        try {
+            ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Offer returns true
+     */
+    public void testOffer() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        assertTrue(q.offer(zero));
+        assertTrue(q.offer(one));
+    }
+
+    /**
+     * add returns true
+     */
+    public void testAdd() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            assertTrue(q.add(new Integer(i)));
+        }
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        try {
+            ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        try {
+            ConcurrentLinkedQueue q = populatedQueue(SIZE);
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        try {
+            ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+            Integer[] ints = new Integer[SIZE];
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        try {
+            ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i);
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i-1));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i+1));
+            assertFalse(q.contains(i+1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        ConcurrentLinkedQueue p = new ConcurrentLinkedQueue();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if change
+     */
+    public void testRetainAll() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        ConcurrentLinkedQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE-i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            ConcurrentLinkedQueue q = populatedQueue(SIZE);
+            ConcurrentLinkedQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE-i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer I = (Integer)(p.remove());
+                assertFalse(q.contains(I));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements in FIFO order
+     */
+    public void testToArray() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        ConcurrentLinkedQueue<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.poll());
+    }
+
+    /**
+     * toArray(null) throws NullPointerException
+     */
+    public void testToArray_NullArg() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+
+        assertEquals("queue should be empty again", 0, q.size());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+        it = q.iterator();
+        assertSame(it.next(), two);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized queue has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListMapTest.java b/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListMapTest.java
new file mode 100644
index 0000000..4359287
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListMapTest.java
@@ -0,0 +1,1263 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+public class ConcurrentSkipListMapTest extends JSR166TestCase {
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static ConcurrentSkipListMap map5() {
+        ConcurrentSkipListMap map = new ConcurrentSkipListMap();
+        assertTrue(map.isEmpty());
+        map.put(one, "A");
+        map.put(five, "E");
+        map.put(three, "C");
+        map.put(two, "B");
+        map.put(four, "D");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map;
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testClear() {
+        ConcurrentSkipListMap map = map5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * copy constructor creates map equal to source map
+     */
+    public void testConstructFromSorted() {
+        ConcurrentSkipListMap map = map5();
+        ConcurrentSkipListMap map2 = new ConcurrentSkipListMap(map);
+        assertEquals(map, map2);
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testEquals() {
+        ConcurrentSkipListMap map1 = map5();
+        ConcurrentSkipListMap map2 = map5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testContainsKey() {
+        ConcurrentSkipListMap map = map5();
+        assertTrue(map.containsKey(one));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testContainsValue() {
+        ConcurrentSkipListMap map = map5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testGet() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals("A", (String)map.get(one));
+        ConcurrentSkipListMap empty = new ConcurrentSkipListMap();
+        assertNull(empty.get(one));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testIsEmpty() {
+        ConcurrentSkipListMap empty = new ConcurrentSkipListMap();
+        ConcurrentSkipListMap map = map5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testFirstKey() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals(one, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testLastKey() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals(five, map.lastKey());
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testKeySetToArray() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * descendingkeySet.toArray returns contains all keys
+     */
+    public void testDescendingKeySetToArray() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.descendingKeySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testKeySet() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(one));
+        assertTrue(s.contains(two));
+        assertTrue(s.contains(three));
+        assertTrue(s.contains(four));
+        assertTrue(s.contains(five));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testKeySetOrder() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descending iterator of key set is inverse ordered
+     */
+    public void testKeySetDescendingIteratorOrder() {
+        ConcurrentSkipListMap map = map5();
+        NavigableSet s = map.navigableKeySet();
+        Iterator i = s.descendingIterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, five);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descendingKeySet is ordered
+     */
+    public void testDescendingKeySetOrder() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.descendingKeySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, five);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descending iterator of descendingKeySet is ordered
+     */
+    public void testDescendingKeySetDescendingIteratorOrder() {
+        ConcurrentSkipListMap map = map5();
+        NavigableSet s = map.descendingKeySet();
+        Iterator i = s.descendingIterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * Values.toArray contains all values
+     */
+    public void testValuesToArray() {
+        ConcurrentSkipListMap map = map5();
+        Collection v = map.values();
+        Object[] ar = v.toArray();
+        ArrayList s = new ArrayList(Arrays.asList(ar));
+        assertEquals(5, ar.length);
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testValues() {
+        ConcurrentSkipListMap map = map5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testEntrySet() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * descendingEntrySet contains all pairs
+     */
+    public void testDescendingEntrySet() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.descendingMap().entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * entrySet.toArray contains all entries
+     */
+    public void testEntrySetToArray() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.entrySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        for (int i = 0; i < 5; ++i) {
+            assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
+            assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
+        }
+    }
+
+    /**
+     * descendingEntrySet.toArray contains all entries
+     */
+    public void testDescendingEntrySetToArray() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.descendingMap().entrySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        for (int i = 0; i < 5; ++i) {
+            assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
+            assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testPutAll() {
+        ConcurrentSkipListMap empty = new ConcurrentSkipListMap();
+        ConcurrentSkipListMap map = map5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(one));
+        assertTrue(empty.containsKey(two));
+        assertTrue(empty.containsKey(three));
+        assertTrue(empty.containsKey(four));
+        assertTrue(empty.containsKey(five));
+    }
+
+    /**
+     * putIfAbsent works when the given key is not present
+     */
+    public void testPutIfAbsent() {
+        ConcurrentSkipListMap map = map5();
+        map.putIfAbsent(six, "Z");
+        assertTrue(map.containsKey(six));
+    }
+
+    /**
+     * putIfAbsent does not add the pair if the key is already present
+     */
+    public void testPutIfAbsent2() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals("A", map.putIfAbsent(one, "Z"));
+    }
+
+    /**
+     * replace fails when the given key is not present
+     */
+    public void testReplace() {
+        ConcurrentSkipListMap map = map5();
+        assertNull(map.replace(six, "Z"));
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * replace succeeds if the key is already present
+     */
+    public void testReplace2() {
+        ConcurrentSkipListMap map = map5();
+        assertNotNull(map.replace(one, "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * replace value fails when the given key not mapped to expected value
+     */
+    public void testReplaceValue() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals("A", map.get(one));
+        assertFalse(map.replace(one, "Z", "Z"));
+        assertEquals("A", map.get(one));
+    }
+
+    /**
+     * replace value succeeds when the given key mapped to expected value
+     */
+    public void testReplaceValue2() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals("A", map.get(one));
+        assertTrue(map.replace(one, "A", "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testRemove() {
+        ConcurrentSkipListMap map = map5();
+        map.remove(five);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+    }
+
+    /**
+     * remove(key,value) removes only if pair present
+     */
+    public void testRemove2() {
+        ConcurrentSkipListMap map = map5();
+        assertTrue(map.containsKey(five));
+        assertEquals("E", map.get(five));
+        map.remove(five, "E");
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+        map.remove(four, "A");
+        assertEquals(4, map.size());
+        assertTrue(map.containsKey(four));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testLowerEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e1 = map.lowerEntry(three);
+        assertEquals(two, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(one);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testHigherEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e1 = map.higherEntry(three);
+        assertEquals(four, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(five);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testFloorEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e1 = map.floorEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(one);
+        assertEquals(one, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testCeilingEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e1 = map.ceilingEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(five);
+        assertEquals(five, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * lowerEntry, higherEntry, ceilingEntry, and floorEntry return
+     * immutable entries
+     */
+    public void testEntryImmutability() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e = map.lowerEntry(three);
+        assertEquals(two, e.getKey());
+        try {
+            e.setValue("X");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.higherEntry(zero);
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("X");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.floorEntry(one);
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("X");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.ceilingEntry(five);
+        assertEquals(five, e.getKey());
+        try {
+            e.setValue("X");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+
+    /**
+     * lowerKey returns preceding element
+     */
+    public void testLowerKey() {
+        ConcurrentSkipListMap q = map5();
+        Object e1 = q.lowerKey(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lowerKey(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lowerKey(one);
+        assertNull(e3);
+
+        Object e4 = q.lowerKey(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherKey returns next element
+     */
+    public void testHigherKey() {
+        ConcurrentSkipListMap q = map5();
+        Object e1 = q.higherKey(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higherKey(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higherKey(five);
+        assertNull(e3);
+
+        Object e4 = q.higherKey(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorKey returns preceding element
+     */
+    public void testFloorKey() {
+        ConcurrentSkipListMap q = map5();
+        Object e1 = q.floorKey(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floorKey(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floorKey(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floorKey(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingKey returns next element
+     */
+    public void testCeilingKey() {
+        ConcurrentSkipListMap q = map5();
+        Object e1 = q.ceilingKey(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceilingKey(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceilingKey(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceilingKey(six);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testPollFirstEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(two, e.getKey());
+        map.put(one, "A");
+        e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(three, e.getKey());
+        map.remove(four);
+        e = map.pollFirstEntry();
+        assertEquals(five, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testPollLastEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(four, e.getKey());
+        map.put(five, "E");
+        e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(three, e.getKey());
+        map.remove(two);
+        e = map.pollLastEntry();
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testSize() {
+        ConcurrentSkipListMap map = map5();
+        ConcurrentSkipListMap empty = new ConcurrentSkipListMap();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        ConcurrentSkipListMap map = map5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception tests
+
+    /**
+     * get(null) of nonempty map throws NPE
+     */
+    public void testGet_NullPointerException() {
+        try {
+            ConcurrentSkipListMap c = map5();
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) of nonempty map throws NPE
+     */
+    public void testContainsKey_NullPointerException() {
+        try {
+            ConcurrentSkipListMap c = map5();
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsValue(null) throws NPE
+     */
+    public void testContainsValue_NullPointerException() {
+        try {
+            ConcurrentSkipListMap c = new ConcurrentSkipListMap();
+            c.containsValue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testPut1_NullPointerException() {
+        try {
+            ConcurrentSkipListMap c = map5();
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * putIfAbsent(null, x) throws NPE
+     */
+    public void testPutIfAbsent1_NullPointerException() {
+        try {
+            ConcurrentSkipListMap c = map5();
+            c.putIfAbsent(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x) throws NPE
+     */
+    public void testReplace_NullPointerException() {
+        try {
+            ConcurrentSkipListMap c = map5();
+            c.replace(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x, y) throws NPE
+     */
+    public void testReplaceValue_NullPointerException() {
+        try {
+            ConcurrentSkipListMap c = map5();
+            c.replace(null, one, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE
+     */
+    public void testRemove1_NullPointerException() {
+        try {
+            ConcurrentSkipListMap c = new ConcurrentSkipListMap();
+            c.put("sadsdf", "asdads");
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null, x) throws NPE
+     */
+    public void testRemove2_NullPointerException() {
+        try {
+            ConcurrentSkipListMap c = new ConcurrentSkipListMap();
+            c.put("sadsdf", "asdads");
+            c.remove(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(x, null) returns false
+     */
+    public void testRemove3() {
+        ConcurrentSkipListMap c = new ConcurrentSkipListMap();
+        c.put("sadsdf", "asdads");
+        assertFalse(c.remove("sadsdf", null));
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testSerialization() throws Exception {
+        NavigableMap x = map5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testSubMapContents() {
+        ConcurrentSkipListMap map = map5();
+        NavigableMap sm = map.subMap(two, true, four, false);
+        assertEquals(two, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(three, k);
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals("C", sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testSubMapContents2() {
+        ConcurrentSkipListMap map = map5();
+        NavigableMap sm = map.subMap(two, true, three, false);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.firstKey());
+        assertEquals(two, sm.lastKey());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertFalse(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(three), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testHeadMapContents() {
+        ConcurrentSkipListMap map = map5();
+        NavigableMap sm = map.headMap(four, false);
+        assertTrue(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(four, map.firstKey());
+    }
+
+    /**
+     * tailMap returns map with keys in requested range
+     */
+    public void testTailMapContents() {
+        ConcurrentSkipListMap map = map5();
+        NavigableMap sm = map.tailMap(two, true);
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertTrue(sm.containsKey(four));
+        assertTrue(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(five, k);
+        k = (Integer)(r.next());
+        assertEquals(four, k);
+        k = (Integer)(r.next());
+        assertEquals(three, k);
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(two, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(three, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(four, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        NavigableMap ssm = sm.tailMap(four, true);
+        assertEquals(four, ssm.firstKey());
+        assertEquals(five, ssm.lastKey());
+        assertEquals("D", ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+    Random rnd = new Random(666);
+    BitSet bs;
+
+    /**
+     * Submaps of submaps subdivide correctly
+     */
+    public void testRecursiveSubMaps() throws Exception {
+        int mapSize = expensiveTests ? 1000 : 100;
+        Class cl = ConcurrentSkipListMap.class;
+        NavigableMap<Integer, Integer> map = newMap(cl);
+        bs = new BitSet(mapSize);
+
+        populate(map, mapSize);
+        check(map,                 0, mapSize - 1, true);
+        check(map.descendingMap(), 0, mapSize - 1, false);
+
+        mutateMap(map, 0, mapSize - 1);
+        check(map,                 0, mapSize - 1, true);
+        check(map.descendingMap(), 0, mapSize - 1, false);
+
+        bashSubMap(map.subMap(0, true, mapSize, false),
+                   0, mapSize - 1, true);
+    }
+
+    static NavigableMap<Integer, Integer> newMap(Class cl) throws Exception {
+        NavigableMap<Integer, Integer> result =
+            (NavigableMap<Integer, Integer>) cl.newInstance();
+        assertEquals(0, result.size());
+        assertFalse(result.keySet().iterator().hasNext());
+        return result;
+    }
+
+    void populate(NavigableMap<Integer, Integer> map, int limit) {
+        for (int i = 0, n = 2 * limit / 3; i < n; i++) {
+            int key = rnd.nextInt(limit);
+            put(map, key);
+        }
+    }
+
+    void mutateMap(NavigableMap<Integer, Integer> map, int min, int max) {
+        int size = map.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(map, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = map.keySet().iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (map.size() < size) {
+            int key = min + rnd.nextInt(rangeSize);
+            assertTrue(key >= min && key<= max);
+            put(map, key);
+        }
+    }
+
+    void mutateSubMap(NavigableMap<Integer, Integer> map, int min, int max) {
+        int size = map.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(map, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = map.keySet().iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (map.size() < size) {
+            int key = min - 5 + rnd.nextInt(rangeSize + 10);
+            if (key >= min && key<= max) {
+                put(map, key);
+            } else {
+                try {
+                    map.put(key, 2 * key);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+            }
+        }
+    }
+
+    void put(NavigableMap<Integer, Integer> map, int key) {
+        if (map.put(key, 2 * key) == null)
+            bs.set(key);
+    }
+
+    void remove(NavigableMap<Integer, Integer> map, int key) {
+        if (map.remove(key) != null)
+            bs.clear(key);
+    }
+
+    void bashSubMap(NavigableMap<Integer, Integer> map,
+                    int min, int max, boolean ascending) {
+        check(map, min, max, ascending);
+        check(map.descendingMap(), min, max, !ascending);
+
+        mutateSubMap(map, min, max);
+        check(map, min, max, ascending);
+        check(map.descendingMap(), min, max, !ascending);
+
+        // Recurse
+        if (max - min < 2)
+            return;
+        int midPoint = (min + max) / 2;
+
+        // headMap - pick direction and endpoint inclusion randomly
+        boolean incl = rnd.nextBoolean();
+        NavigableMap<Integer,Integer> hm = map.headMap(midPoint, incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubMap(hm, min, midPoint - (incl ? 0 : 1), true);
+            else
+                bashSubMap(hm.descendingMap(), min, midPoint - (incl ? 0 : 1),
+                           false);
+        } else {
+            if (rnd.nextBoolean())
+                bashSubMap(hm, midPoint + (incl ? 0 : 1), max, false);
+            else
+                bashSubMap(hm.descendingMap(), midPoint + (incl ? 0 : 1), max,
+                           true);
+        }
+
+        // tailMap - pick direction and endpoint inclusion randomly
+        incl = rnd.nextBoolean();
+        NavigableMap<Integer,Integer> tm = map.tailMap(midPoint,incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubMap(tm, midPoint + (incl ? 0 : 1), max, true);
+            else
+                bashSubMap(tm.descendingMap(), midPoint + (incl ? 0 : 1), max,
+                           false);
+        } else {
+            if (rnd.nextBoolean()) {
+                bashSubMap(tm, min, midPoint - (incl ? 0 : 1), false);
+            } else {
+                bashSubMap(tm.descendingMap(), min, midPoint - (incl ? 0 : 1),
+                           true);
+            }
+        }
+
+        // subMap - pick direction and endpoint inclusion randomly
+        int rangeSize = max - min + 1;
+        int[] endpoints = new int[2];
+        endpoints[0] = min + rnd.nextInt(rangeSize);
+        endpoints[1] = min + rnd.nextInt(rangeSize);
+        Arrays.sort(endpoints);
+        boolean lowIncl = rnd.nextBoolean();
+        boolean highIncl = rnd.nextBoolean();
+        if (ascending) {
+            NavigableMap<Integer,Integer> sm = map.subMap(
+                endpoints[0], lowIncl, endpoints[1], highIncl);
+            if (rnd.nextBoolean())
+                bashSubMap(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+            else
+                bashSubMap(sm.descendingMap(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+        } else {
+            NavigableMap<Integer,Integer> sm = map.subMap(
+                endpoints[1], highIncl, endpoints[0], lowIncl);
+            if (rnd.nextBoolean())
+                bashSubMap(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+            else
+                bashSubMap(sm.descendingMap(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+        }
+    }
+
+    /**
+     * min and max are both inclusive.  If max < min, interval is empty.
+     */
+    void check(NavigableMap<Integer, Integer> map,
+                      final int min, final int max, final boolean ascending) {
+        class ReferenceSet {
+            int lower(int key) {
+                return ascending ? lowerAscending(key) : higherAscending(key);
+            }
+            int floor(int key) {
+                return ascending ? floorAscending(key) : ceilingAscending(key);
+            }
+            int ceiling(int key) {
+                return ascending ? ceilingAscending(key) : floorAscending(key);
+            }
+            int higher(int key) {
+                return ascending ? higherAscending(key) : lowerAscending(key);
+            }
+            int first() {
+                return ascending ? firstAscending() : lastAscending();
+            }
+            int last() {
+                return ascending ? lastAscending() : firstAscending();
+            }
+            int lowerAscending(int key) {
+                return floorAscending(key - 1);
+            }
+            int floorAscending(int key) {
+                if (key < min)
+                    return -1;
+                else if (key > max)
+                    key = max;
+
+                // BitSet should support this! Test would run much faster
+                while (key >= min) {
+                    if (bs.get(key))
+                        return key;
+                    key--;
+                }
+                return -1;
+            }
+            int ceilingAscending(int key) {
+                if (key < min)
+                    key = min;
+                else if (key > max)
+                    return -1;
+                int result = bs.nextSetBit(key);
+                return result > max ? -1 : result;
+            }
+            int higherAscending(int key) {
+                return ceilingAscending(key + 1);
+            }
+            private int firstAscending() {
+                int result = ceilingAscending(min);
+                return result > max ? -1 : result;
+            }
+            private int lastAscending() {
+                int result = floorAscending(max);
+                return result < min ? -1 : result;
+            }
+        }
+        ReferenceSet rs = new ReferenceSet();
+
+        // Test contents using containsKey
+        int size = 0;
+        for (int i = min; i <= max; i++) {
+            boolean bsContainsI = bs.get(i);
+            assertEquals(bsContainsI, map.containsKey(i));
+            if (bsContainsI)
+                size++;
+        }
+        assertEquals(size, map.size());
+
+        // Test contents using contains keySet iterator
+        int size2 = 0;
+        int previousKey = -1;
+        for (int key : map.keySet()) {
+            assertTrue(bs.get(key));
+            size2++;
+            assertTrue(previousKey < 0 ||
+                (ascending ? key - previousKey > 0 : key - previousKey < 0));
+            previousKey = key;
+        }
+        assertEquals(size2, size);
+
+        // Test navigation ops
+        for (int key = min - 1; key <= max + 1; key++) {
+            assertEq(map.lowerKey(key), rs.lower(key));
+            assertEq(map.floorKey(key), rs.floor(key));
+            assertEq(map.higherKey(key), rs.higher(key));
+            assertEq(map.ceilingKey(key), rs.ceiling(key));
+        }
+
+        // Test extrema
+        if (map.size() != 0) {
+            assertEq(map.firstKey(), rs.first());
+            assertEq(map.lastKey(), rs.last());
+        } else {
+            assertEq(rs.first(), -1);
+            assertEq(rs.last(),  -1);
+            try {
+                map.firstKey();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                map.lastKey();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+        }
+    }
+
+    static void assertEq(Integer i, int j) {
+        if (i == null)
+            assertEquals(j, -1);
+        else
+            assertEquals((int) i, j);
+    }
+
+    static boolean eq(Integer i, int j) {
+        return i == null ? j == -1 : i == j;
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSetTest.java b/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSetTest.java
new file mode 100644
index 0000000..1fd3c5f
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSetTest.java
@@ -0,0 +1,982 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NavigableSet;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.concurrent.ConcurrentSkipListSet;
+
+public class ConcurrentSkipListSetTest extends JSR166TestCase {
+
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * Returns a new set of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private ConcurrentSkipListSet<Integer> populatedSet(int n) {
+        ConcurrentSkipListSet<Integer> q =
+            new ConcurrentSkipListSet<Integer>();
+        assertTrue(q.isEmpty());
+        for (int i = n-1; i >= 0; i-=2)
+            assertTrue(q.add(new Integer(i)));
+        for (int i = (n & 1); i < n; i+=2)
+            assertTrue(q.add(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * Returns a new set of first 5 ints.
+     */
+    private ConcurrentSkipListSet set5() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        q.add(four);
+        q.add(five);
+        assertEquals(5, q.size());
+        return q;
+    }
+
+    /**
+     * A new set has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(0, new ConcurrentSkipListSet().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            ConcurrentSkipListSet q = new ConcurrentSkipListSet((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            Integer[] ints = new Integer[SIZE];
+            ConcurrentSkipListSet q = new ConcurrentSkipListSet(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        try {
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i);
+            ConcurrentSkipListSet q = new ConcurrentSkipListSet(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * The comparator used in constructor is used
+     */
+    public void testConstructor7() {
+        MyReverseComparator cmp = new MyReverseComparator();
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet(cmp);
+        assertEquals(cmp, q.comparator());
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        q.addAll(Arrays.asList(ints));
+        for (int i = SIZE-1; i >= 0; --i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.pollFirst();
+        q.pollFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        try {
+            ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testAdd() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.add(zero));
+        assertTrue(q.add(one));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testAddDup() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.add(zero));
+        assertFalse(q.add(zero));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testAddNonComparable() {
+        try {
+            ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+            q.add(new Object());
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        try {
+            ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        try {
+            ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+            Integer[] ints = new Integer[SIZE];
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        try {
+            ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i);
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE-1-i);
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(i, q.pollFirst());
+    }
+
+    /**
+     * pollFirst succeeds unless empty
+     */
+    public void testPollFirst() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * pollLast succeeds unless empty
+     */
+    public void testPollLast() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        for (int i = SIZE-1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i-1));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i+1));
+            assertFalse(q.contains(i+1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        ConcurrentSkipListSet p = new ConcurrentSkipListSet();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        ConcurrentSkipListSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE-i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            ConcurrentSkipListSet q = populatedSet(SIZE);
+            ConcurrentSkipListSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE-i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer I = (Integer)(p.pollFirst());
+                assertFalse(q.contains(I));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testLower() {
+        ConcurrentSkipListSet q = set5();
+        Object e1 = q.lower(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lower(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lower(one);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testHigher() {
+        ConcurrentSkipListSet q = set5();
+        Object e1 = q.higher(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higher(five);
+        assertNull(e3);
+
+        Object e4 = q.higher(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testFloor() {
+        ConcurrentSkipListSet q = set5();
+        Object e1 = q.floor(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floor(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floor(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testCeiling() {
+        ConcurrentSkipListSet q = set5();
+        Object e1 = q.ceiling(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceiling(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceiling(six);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements in sorted order
+     */
+    public void testToArray() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements in sorted order
+     */
+    public void testToArray2() {
+        ConcurrentSkipListSet<Integer> q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        assertSame(ints, q.toArray(ints));
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testEmptyIterator() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(0, i);
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testSerialization() throws Exception {
+        NavigableSet x = populatedSet(SIZE);
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testSubSetContents() {
+        ConcurrentSkipListSet set = set5();
+        SortedSet sm = set.subSet(two, four);
+        assertEquals(two, sm.first());
+        assertEquals(three, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.first());
+        assertEquals(three, sm.last());
+        assertTrue(sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testSubSetContents2() {
+        ConcurrentSkipListSet set = set5();
+        SortedSet sm = set.subSet(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.first());
+        assertEquals(two, sm.last());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertFalse(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(three));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testHeadSetContents() {
+        ConcurrentSkipListSet set = set5();
+        SortedSet sm = set.headSet(four);
+        assertTrue(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(four, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testTailSetContents() {
+        ConcurrentSkipListSet set = set5();
+        SortedSet sm = set.tailSet(two);
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertTrue(sm.contains(four));
+        assertTrue(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(four);
+        assertEquals(four, ssm.first());
+        assertEquals(five, ssm.last());
+        assertTrue(ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+    Random rnd = new Random(666);
+
+    /**
+     * Subsets of subsets subdivide correctly
+     */
+    public void testRecursiveSubSets() throws Exception {
+        int setSize = expensiveTests ? 1000 : 100;
+        Class cl = ConcurrentSkipListSet.class;
+
+        NavigableSet<Integer> set = newSet(cl);
+        BitSet bs = new BitSet(setSize);
+
+        populate(set, setSize, bs);
+        check(set,                 0, setSize - 1, true, bs);
+        check(set.descendingSet(), 0, setSize - 1, false, bs);
+
+        mutateSet(set, 0, setSize - 1, bs);
+        check(set,                 0, setSize - 1, true, bs);
+        check(set.descendingSet(), 0, setSize - 1, false, bs);
+
+        bashSubSet(set.subSet(0, true, setSize, false),
+                   0, setSize - 1, true, bs);
+    }
+
+    /**
+     * addAll is idempotent
+     */
+    public void testAddAll_idempotent() throws Exception {
+        Set x = populatedSet(SIZE);
+        Set y = new ConcurrentSkipListSet(x);
+        y.addAll(x);
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    static NavigableSet<Integer> newSet(Class cl) throws Exception {
+        NavigableSet<Integer> result = (NavigableSet<Integer>) cl.newInstance();
+        assertEquals(0, result.size());
+        assertFalse(result.iterator().hasNext());
+        return result;
+    }
+
+    void populate(NavigableSet<Integer> set, int limit, BitSet bs) {
+        for (int i = 0, n = 2 * limit / 3; i < n; i++) {
+            int element = rnd.nextInt(limit);
+            put(set, element, bs);
+        }
+    }
+
+    void mutateSet(NavigableSet<Integer> set, int min, int max, BitSet bs) {
+        int size = set.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(set, min - 5 + rnd.nextInt(rangeSize + 10), bs);
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (set.size() < size) {
+            int element = min + rnd.nextInt(rangeSize);
+            assertTrue(element >= min && element<= max);
+            put(set, element, bs);
+        }
+    }
+
+    void mutateSubSet(NavigableSet<Integer> set, int min, int max,
+                      BitSet bs) {
+        int size = set.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(set, min - 5 + rnd.nextInt(rangeSize + 10), bs);
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (set.size() < size) {
+            int element = min - 5 + rnd.nextInt(rangeSize + 10);
+            if (element >= min && element<= max) {
+                put(set, element, bs);
+            } else {
+                try {
+                    set.add(element);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+            }
+        }
+    }
+
+    void put(NavigableSet<Integer> set, int element, BitSet bs) {
+        if (set.add(element))
+            bs.set(element);
+    }
+
+    void remove(NavigableSet<Integer> set, int element, BitSet bs) {
+        if (set.remove(element))
+            bs.clear(element);
+    }
+
+    void bashSubSet(NavigableSet<Integer> set,
+                    int min, int max, boolean ascending,
+                    BitSet bs) {
+        check(set, min, max, ascending, bs);
+        check(set.descendingSet(), min, max, !ascending, bs);
+
+        mutateSubSet(set, min, max, bs);
+        check(set, min, max, ascending, bs);
+        check(set.descendingSet(), min, max, !ascending, bs);
+
+        // Recurse
+        if (max - min < 2)
+            return;
+        int midPoint = (min + max) / 2;
+
+        // headSet - pick direction and endpoint inclusion randomly
+        boolean incl = rnd.nextBoolean();
+        NavigableSet<Integer> hm = set.headSet(midPoint, incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubSet(hm, min, midPoint - (incl ? 0 : 1), true, bs);
+            else
+                bashSubSet(hm.descendingSet(), min, midPoint - (incl ? 0 : 1),
+                           false, bs);
+        } else {
+            if (rnd.nextBoolean())
+                bashSubSet(hm, midPoint + (incl ? 0 : 1), max, false, bs);
+            else
+                bashSubSet(hm.descendingSet(), midPoint + (incl ? 0 : 1), max,
+                           true, bs);
+        }
+
+        // tailSet - pick direction and endpoint inclusion randomly
+        incl = rnd.nextBoolean();
+        NavigableSet<Integer> tm = set.tailSet(midPoint,incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubSet(tm, midPoint + (incl ? 0 : 1), max, true, bs);
+            else
+                bashSubSet(tm.descendingSet(), midPoint + (incl ? 0 : 1), max,
+                           false, bs);
+        } else {
+            if (rnd.nextBoolean()) {
+                bashSubSet(tm, min, midPoint - (incl ? 0 : 1), false, bs);
+            } else {
+                bashSubSet(tm.descendingSet(), min, midPoint - (incl ? 0 : 1),
+                           true, bs);
+            }
+        }
+
+        // subSet - pick direction and endpoint inclusion randomly
+        int rangeSize = max - min + 1;
+        int[] endpoints = new int[2];
+        endpoints[0] = min + rnd.nextInt(rangeSize);
+        endpoints[1] = min + rnd.nextInt(rangeSize);
+        Arrays.sort(endpoints);
+        boolean lowIncl = rnd.nextBoolean();
+        boolean highIncl = rnd.nextBoolean();
+        if (ascending) {
+            NavigableSet<Integer> sm = set.subSet(
+                endpoints[0], lowIncl, endpoints[1], highIncl);
+            if (rnd.nextBoolean())
+                bashSubSet(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true, bs);
+            else
+                bashSubSet(sm.descendingSet(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false, bs);
+        } else {
+            NavigableSet<Integer> sm = set.subSet(
+                endpoints[1], highIncl, endpoints[0], lowIncl);
+            if (rnd.nextBoolean())
+                bashSubSet(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false, bs);
+            else
+                bashSubSet(sm.descendingSet(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true, bs);
+        }
+    }
+
+    /**
+     * min and max are both inclusive.  If max < min, interval is empty.
+     */
+    void check(NavigableSet<Integer> set,
+               final int min, final int max, final boolean ascending,
+               final BitSet bs) {
+        class ReferenceSet {
+            int lower(int element) {
+                return ascending ?
+                    lowerAscending(element) : higherAscending(element);
+            }
+            int floor(int element) {
+                return ascending ?
+                    floorAscending(element) : ceilingAscending(element);
+            }
+            int ceiling(int element) {
+                return ascending ?
+                    ceilingAscending(element) : floorAscending(element);
+            }
+            int higher(int element) {
+                return ascending ?
+                    higherAscending(element) : lowerAscending(element);
+            }
+            int first() {
+                return ascending ? firstAscending() : lastAscending();
+            }
+            int last() {
+                return ascending ? lastAscending() : firstAscending();
+            }
+            int lowerAscending(int element) {
+                return floorAscending(element - 1);
+            }
+            int floorAscending(int element) {
+                if (element < min)
+                    return -1;
+                else if (element > max)
+                    element = max;
+
+                // BitSet should support this! Test would run much faster
+                while (element >= min) {
+                    if (bs.get(element))
+                        return element;
+                    element--;
+                }
+                return -1;
+            }
+            int ceilingAscending(int element) {
+                if (element < min)
+                    element = min;
+                else if (element > max)
+                    return -1;
+                int result = bs.nextSetBit(element);
+                return result > max ? -1 : result;
+            }
+            int higherAscending(int element) {
+                return ceilingAscending(element + 1);
+            }
+            private int firstAscending() {
+                int result = ceilingAscending(min);
+                return result > max ? -1 : result;
+            }
+            private int lastAscending() {
+                int result = floorAscending(max);
+                return result < min ? -1 : result;
+            }
+        }
+        ReferenceSet rs = new ReferenceSet();
+
+        // Test contents using containsElement
+        int size = 0;
+        for (int i = min; i <= max; i++) {
+            boolean bsContainsI = bs.get(i);
+            assertEquals(bsContainsI, set.contains(i));
+            if (bsContainsI)
+                size++;
+        }
+        assertEquals(size, set.size());
+
+        // Test contents using contains elementSet iterator
+        int size2 = 0;
+        int previousElement = -1;
+        for (int element : set) {
+            assertTrue(bs.get(element));
+            size2++;
+            assertTrue(previousElement < 0 || (ascending ?
+                element - previousElement > 0 : element - previousElement < 0));
+            previousElement = element;
+        }
+        assertEquals(size2, size);
+
+        // Test navigation ops
+        for (int element = min - 1; element <= max + 1; element++) {
+            assertEq(set.lower(element), rs.lower(element));
+            assertEq(set.floor(element), rs.floor(element));
+            assertEq(set.higher(element), rs.higher(element));
+            assertEq(set.ceiling(element), rs.ceiling(element));
+        }
+
+        // Test extrema
+        if (set.size() != 0) {
+            assertEq(set.first(), rs.first());
+            assertEq(set.last(), rs.last());
+        } else {
+            assertEq(rs.first(), -1);
+            assertEq(rs.last(),  -1);
+            try {
+                set.first();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                set.last();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+        }
+    }
+
+    static void assertEq(Integer i, int j) {
+        if (i == null)
+            assertEquals(j, -1);
+        else
+            assertEquals((int) i, j);
+    }
+
+    static boolean eq(Integer i, int j) {
+        return i == null ? j == -1 : i == j;
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSubMapTest.java b/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSubMapTest.java
new file mode 100644
index 0000000..7247657
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSubMapTest.java
@@ -0,0 +1,1410 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.ConcurrentNavigableMap;
+import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.*;
+
+public class ConcurrentSkipListSubMapTest extends JSR166TestCase {
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static ConcurrentNavigableMap map5() {
+        ConcurrentSkipListMap map = new ConcurrentSkipListMap();
+        assertTrue(map.isEmpty());
+        map.put(zero, "Z");
+        map.put(one, "A");
+        map.put(five, "E");
+        map.put(three, "C");
+        map.put(two, "B");
+        map.put(four, "D");
+        map.put(seven, "F");
+        assertFalse(map.isEmpty());
+        assertEquals(7, map.size());
+        return map.subMap(one, true, seven, false);
+    }
+
+    /**
+     * Returns a new map from Integers -5 to -1 to Strings "A"-"E".
+     */
+    private static ConcurrentNavigableMap dmap5() {
+        ConcurrentSkipListMap map = new ConcurrentSkipListMap();
+        assertTrue(map.isEmpty());
+        map.put(m1, "A");
+        map.put(m5, "E");
+        map.put(m3, "C");
+        map.put(m2, "B");
+        map.put(m4, "D");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map.descendingMap();
+    }
+
+    private static ConcurrentNavigableMap map0() {
+        ConcurrentSkipListMap map = new ConcurrentSkipListMap();
+        assertTrue(map.isEmpty());
+        return map.tailMap(one, true);
+    }
+
+    private static ConcurrentNavigableMap dmap0() {
+        ConcurrentSkipListMap map = new ConcurrentSkipListMap();
+        assertTrue(map.isEmpty());
+        return map;
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testClear() {
+        ConcurrentNavigableMap map = map5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testEquals() {
+        ConcurrentNavigableMap map1 = map5();
+        ConcurrentNavigableMap map2 = map5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testContainsKey() {
+        ConcurrentNavigableMap map = map5();
+        assertTrue(map.containsKey(one));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testContainsValue() {
+        ConcurrentNavigableMap map = map5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testGet() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals("A", (String)map.get(one));
+        ConcurrentNavigableMap empty = map0();
+        assertNull(empty.get(one));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testIsEmpty() {
+        ConcurrentNavigableMap empty = map0();
+        ConcurrentNavigableMap map = map5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testFirstKey() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals(one, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testLastKey() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals(five, map.lastKey());
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testKeySet() {
+        ConcurrentNavigableMap map = map5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(one));
+        assertTrue(s.contains(two));
+        assertTrue(s.contains(three));
+        assertTrue(s.contains(four));
+        assertTrue(s.contains(five));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testKeySetOrder() {
+        ConcurrentNavigableMap map = map5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+        }
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testValues() {
+        ConcurrentNavigableMap map = map5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testKeySetToArray() {
+        ConcurrentNavigableMap map = map5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * descendingkeySet.toArray returns contains all keys
+     */
+    public void testDescendingKeySetToArray() {
+        ConcurrentNavigableMap map = map5();
+        Set s = map.descendingKeySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * Values.toArray contains all values
+     */
+    public void testValuesToArray() {
+        ConcurrentNavigableMap map = map5();
+        Collection v = map.values();
+        Object[] ar = v.toArray();
+        ArrayList s = new ArrayList(Arrays.asList(ar));
+        assertEquals(5, ar.length);
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testEntrySet() {
+        ConcurrentNavigableMap map = map5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testPutAll() {
+        ConcurrentNavigableMap empty = map0();
+        ConcurrentNavigableMap map = map5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(one));
+        assertTrue(empty.containsKey(two));
+        assertTrue(empty.containsKey(three));
+        assertTrue(empty.containsKey(four));
+        assertTrue(empty.containsKey(five));
+    }
+
+    /**
+     * putIfAbsent works when the given key is not present
+     */
+    public void testPutIfAbsent() {
+        ConcurrentNavigableMap map = map5();
+        map.putIfAbsent(six, "Z");
+        assertTrue(map.containsKey(six));
+    }
+
+    /**
+     * putIfAbsent does not add the pair if the key is already present
+     */
+    public void testPutIfAbsent2() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals("A", map.putIfAbsent(one, "Z"));
+    }
+
+    /**
+     * replace fails when the given key is not present
+     */
+    public void testReplace() {
+        ConcurrentNavigableMap map = map5();
+        assertNull(map.replace(six, "Z"));
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * replace succeeds if the key is already present
+     */
+    public void testReplace2() {
+        ConcurrentNavigableMap map = map5();
+        assertNotNull(map.replace(one, "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * replace value fails when the given key not mapped to expected value
+     */
+    public void testReplaceValue() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals("A", map.get(one));
+        assertFalse(map.replace(one, "Z", "Z"));
+        assertEquals("A", map.get(one));
+    }
+
+    /**
+     * replace value succeeds when the given key mapped to expected value
+     */
+    public void testReplaceValue2() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals("A", map.get(one));
+        assertTrue(map.replace(one, "A", "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testRemove() {
+        ConcurrentNavigableMap map = map5();
+        map.remove(five);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+    }
+
+    /**
+     * remove(key,value) removes only if pair present
+     */
+    public void testRemove2() {
+        ConcurrentNavigableMap map = map5();
+        assertTrue(map.containsKey(five));
+        assertEquals("E", map.get(five));
+        map.remove(five, "E");
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+        map.remove(four, "A");
+        assertEquals(4, map.size());
+        assertTrue(map.containsKey(four));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testLowerEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e1 = map.lowerEntry(three);
+        assertEquals(two, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(one);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testHigherEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e1 = map.higherEntry(three);
+        assertEquals(four, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(five);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testFloorEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e1 = map.floorEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(one);
+        assertEquals(one, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testCeilingEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e1 = map.ceilingEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(five);
+        assertEquals(five, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testPollFirstEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(two, e.getKey());
+        map.put(one, "A");
+        e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(three, e.getKey());
+        map.remove(four);
+        e = map.pollFirstEntry();
+        assertEquals(five, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testPollLastEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(four, e.getKey());
+        map.put(five, "E");
+        e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(three, e.getKey());
+        map.remove(two);
+        e = map.pollLastEntry();
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testSize() {
+        ConcurrentNavigableMap map = map5();
+        ConcurrentNavigableMap empty = map0();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        ConcurrentNavigableMap map = map5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception tests
+
+    /**
+     * get(null) of nonempty map throws NPE
+     */
+    public void testGet_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) of nonempty map throws NPE
+     */
+    public void testContainsKey_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsValue(null) throws NPE
+     */
+    public void testContainsValue_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map0();
+            c.containsValue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testPut1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * putIfAbsent(null, x) throws NPE
+     */
+    public void testPutIfAbsent1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.putIfAbsent(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x) throws NPE
+     */
+    public void testReplace_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.replace(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x, y) throws NPE
+     */
+    public void testReplaceValue_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.replace(null, one, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE
+     */
+    public void testRemove1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null, x) throws NPE
+     */
+    public void testRemove2_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.remove(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testSerialization() throws Exception {
+        NavigableMap x = map5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testSubMapContents() {
+        ConcurrentNavigableMap map = map5();
+        SortedMap sm = map.subMap(two, four);
+        assertEquals(two, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals("C", sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testSubMapContents2() {
+        ConcurrentNavigableMap map = map5();
+        SortedMap sm = map.subMap(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.firstKey());
+        assertEquals(two, sm.lastKey());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertFalse(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(three), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testHeadMapContents() {
+        ConcurrentNavigableMap map = map5();
+        SortedMap sm = map.headMap(four);
+        assertTrue(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(four, map.firstKey());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testTailMapContents() {
+        ConcurrentNavigableMap map = map5();
+        SortedMap sm = map.tailMap(two);
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertTrue(sm.containsKey(four));
+        assertTrue(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(two, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(three, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(four, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        SortedMap ssm = sm.tailMap(four);
+        assertEquals(four, ssm.firstKey());
+        assertEquals(five, ssm.lastKey());
+        assertEquals("D", ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testDescendingClear() {
+        ConcurrentNavigableMap map = dmap5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testDescendingEquals() {
+        ConcurrentNavigableMap map1 = dmap5();
+        ConcurrentNavigableMap map2 = dmap5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testDescendingContainsKey() {
+        ConcurrentNavigableMap map = dmap5();
+        assertTrue(map.containsKey(m1));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testDescendingContainsValue() {
+        ConcurrentNavigableMap map = dmap5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testDescendingGet() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals("A", (String)map.get(m1));
+        ConcurrentNavigableMap empty = dmap0();
+        assertNull(empty.get(m1));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testDescendingIsEmpty() {
+        ConcurrentNavigableMap empty = dmap0();
+        ConcurrentNavigableMap map = dmap5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testDescendingFirstKey() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals(m1, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testDescendingLastKey() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals(m5, map.lastKey());
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testDescendingKeySet() {
+        ConcurrentNavigableMap map = dmap5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(m1));
+        assertTrue(s.contains(m2));
+        assertTrue(s.contains(m3));
+        assertTrue(s.contains(m4));
+        assertTrue(s.contains(m5));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testDescendingKeySetOrder() {
+        ConcurrentNavigableMap map = dmap5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, m1);
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+        }
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testDescendingValues() {
+        ConcurrentNavigableMap map = dmap5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testDescendingAscendingKeySetToArray() {
+        ConcurrentNavigableMap map = dmap5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * descendingkeySet.toArray returns contains all keys
+     */
+    public void testDescendingDescendingKeySetToArray() {
+        ConcurrentNavigableMap map = dmap5();
+        Set s = map.descendingKeySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * Values.toArray contains all values
+     */
+    public void testDescendingValuesToArray() {
+        ConcurrentNavigableMap map = dmap5();
+        Collection v = map.values();
+        Object[] ar = v.toArray();
+        ArrayList s = new ArrayList(Arrays.asList(ar));
+        assertEquals(5, ar.length);
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testDescendingEntrySet() {
+        ConcurrentNavigableMap map = dmap5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(m1) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(m2) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(m3) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(m4) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(m5) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testDescendingPutAll() {
+        ConcurrentNavigableMap empty = dmap0();
+        ConcurrentNavigableMap map = dmap5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(m1));
+        assertTrue(empty.containsKey(m2));
+        assertTrue(empty.containsKey(m3));
+        assertTrue(empty.containsKey(m4));
+        assertTrue(empty.containsKey(m5));
+    }
+
+    /**
+     * putIfAbsent works when the given key is not present
+     */
+    public void testDescendingPutIfAbsent() {
+        ConcurrentNavigableMap map = dmap5();
+        map.putIfAbsent(six, "Z");
+        assertTrue(map.containsKey(six));
+    }
+
+    /**
+     * putIfAbsent does not add the pair if the key is already present
+     */
+    public void testDescendingPutIfAbsent2() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals("A", map.putIfAbsent(m1, "Z"));
+    }
+
+    /**
+     * replace fails when the given key is not present
+     */
+    public void testDescendingReplace() {
+        ConcurrentNavigableMap map = dmap5();
+        assertNull(map.replace(six, "Z"));
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * replace succeeds if the key is already present
+     */
+    public void testDescendingReplace2() {
+        ConcurrentNavigableMap map = dmap5();
+        assertNotNull(map.replace(m1, "Z"));
+        assertEquals("Z", map.get(m1));
+    }
+
+    /**
+     * replace value fails when the given key not mapped to expected value
+     */
+    public void testDescendingReplaceValue() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals("A", map.get(m1));
+        assertFalse(map.replace(m1, "Z", "Z"));
+        assertEquals("A", map.get(m1));
+    }
+
+    /**
+     * replace value succeeds when the given key mapped to expected value
+     */
+    public void testDescendingReplaceValue2() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals("A", map.get(m1));
+        assertTrue(map.replace(m1, "A", "Z"));
+        assertEquals("Z", map.get(m1));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testDescendingRemove() {
+        ConcurrentNavigableMap map = dmap5();
+        map.remove(m5);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(m5));
+    }
+
+    /**
+     * remove(key,value) removes only if pair present
+     */
+    public void testDescendingRemove2() {
+        ConcurrentNavigableMap map = dmap5();
+        assertTrue(map.containsKey(m5));
+        assertEquals("E", map.get(m5));
+        map.remove(m5, "E");
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(m5));
+        map.remove(m4, "A");
+        assertEquals(4, map.size());
+        assertTrue(map.containsKey(m4));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testDescendingLowerEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e1 = map.lowerEntry(m3);
+        assertEquals(m2, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(m6);
+        assertEquals(m5, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(m1);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testDescendingHigherEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e1 = map.higherEntry(m3);
+        assertEquals(m4, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(m1, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(m5);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testDescendingFloorEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e1 = map.floorEntry(m3);
+        assertEquals(m3, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(m6);
+        assertEquals(m5, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(m1);
+        assertEquals(m1, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testDescendingCeilingEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e1 = map.ceilingEntry(m3);
+        assertEquals(m3, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(m1, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(m5);
+        assertEquals(m5, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testDescendingPollFirstEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(m1, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(m2, e.getKey());
+        map.put(m1, "A");
+        e = map.pollFirstEntry();
+        assertEquals(m1, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(m3, e.getKey());
+        map.remove(m4);
+        e = map.pollFirstEntry();
+        assertEquals(m5, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testDescendingPollLastEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(m4, e.getKey());
+        map.put(m5, "E");
+        e = map.pollLastEntry();
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(m3, e.getKey());
+        map.remove(m2);
+        e = map.pollLastEntry();
+        assertEquals(m1, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testDescendingSize() {
+        ConcurrentNavigableMap map = dmap5();
+        ConcurrentNavigableMap empty = dmap0();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testDescendingToString() {
+        ConcurrentNavigableMap map = dmap5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception testDescendings
+
+    /**
+     * get(null) of empty map throws NPE
+     */
+    public void testDescendingGet_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) of empty map throws NPE
+     */
+    public void testDescendingContainsKey_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsValue(null) throws NPE
+     */
+    public void testDescendingContainsValue_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap0();
+            c.containsValue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testDescendingPut1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * putIfAbsent(null, x) throws NPE
+     */
+    public void testDescendingPutIfAbsent1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.putIfAbsent(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x) throws NPE
+     */
+    public void testDescendingReplace_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.replace(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x, y) throws NPE
+     */
+    public void testDescendingReplaceValue_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.replace(null, m1, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE
+     */
+    public void testDescendingRemove1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null, x) throws NPE
+     */
+    public void testDescendingRemove2_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.remove(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testDescendingSerialization() throws Exception {
+        NavigableMap x = dmap5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testDescendingSubMapContents() {
+        ConcurrentNavigableMap map = dmap5();
+        SortedMap sm = map.subMap(m2, m4);
+        assertEquals(m2, sm.firstKey());
+        assertEquals(m3, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(m2));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(m3, sm.firstKey());
+        assertEquals(m3, sm.lastKey());
+        assertEquals("C", sm.remove(m3));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testDescendingSubMapContents2() {
+        ConcurrentNavigableMap map = dmap5();
+        SortedMap sm = map.subMap(m2, m3);
+        assertEquals(1, sm.size());
+        assertEquals(m2, sm.firstKey());
+        assertEquals(m2, sm.lastKey());
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertFalse(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(m2));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(m3), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testDescendingHeadMapContents() {
+        ConcurrentNavigableMap map = dmap5();
+        SortedMap sm = map.headMap(m4);
+        assertTrue(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m1, k);
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(m4, map.firstKey());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testDescendingTailMapContents() {
+        ConcurrentNavigableMap map = dmap5();
+        SortedMap sm = map.tailMap(m2);
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertTrue(sm.containsKey(m4));
+        assertTrue(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        k = (Integer)(i.next());
+        assertEquals(m4, k);
+        k = (Integer)(i.next());
+        assertEquals(m5, k);
+        assertFalse(i.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(m2, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m3, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m4, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        SortedMap ssm = sm.tailMap(m4);
+        assertEquals(m4, ssm.firstKey());
+        assertEquals(m5, ssm.lastKey());
+        assertEquals("D", ssm.remove(m4));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSubSetTest.java b/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSubSetTest.java
new file mode 100644
index 0000000..43c1759
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSubSetTest.java
@@ -0,0 +1,1123 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NavigableSet;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.concurrent.ConcurrentSkipListSet;
+
+public class ConcurrentSkipListSubSetTest extends JSR166TestCase {
+
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * Returns a new set of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private NavigableSet<Integer> populatedSet(int n) {
+        ConcurrentSkipListSet<Integer> q =
+            new ConcurrentSkipListSet<Integer>();
+        assertTrue(q.isEmpty());
+
+        for (int i = n-1; i >= 0; i-=2)
+            assertTrue(q.add(new Integer(i)));
+        for (int i = (n & 1); i < n; i+=2)
+            assertTrue(q.add(new Integer(i)));
+        assertTrue(q.add(new Integer(-n)));
+        assertTrue(q.add(new Integer(n)));
+        NavigableSet s = q.subSet(new Integer(0), true, new Integer(n), false);
+        assertFalse(s.isEmpty());
+        assertEquals(n, s.size());
+        return s;
+    }
+
+    /**
+     * Returns a new set of first 5 ints.
+     */
+    private NavigableSet set5() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        q.add(four);
+        q.add(five);
+        q.add(zero);
+        q.add(seven);
+        NavigableSet s = q.subSet(one, true, seven, false);
+        assertEquals(5, s.size());
+        return s;
+    }
+
+    /**
+     * Returns a new set of first 5 negative ints.
+     */
+    private NavigableSet dset5() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.isEmpty());
+        q.add(m1);
+        q.add(m2);
+        q.add(m3);
+        q.add(m4);
+        q.add(m5);
+        NavigableSet s = q.descendingSet();
+        assertEquals(5, s.size());
+        return s;
+    }
+
+    private static NavigableSet set0() {
+        ConcurrentSkipListSet set = new ConcurrentSkipListSet();
+        assertTrue(set.isEmpty());
+        return set.tailSet(m1, true);
+    }
+
+    private static NavigableSet dset0() {
+        ConcurrentSkipListSet set = new ConcurrentSkipListSet();
+        assertTrue(set.isEmpty());
+        return set;
+    }
+
+    /**
+     * A new set has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(0, set0().size());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        NavigableSet q = set0();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.pollFirst();
+        q.pollFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        try {
+            NavigableSet q = set0();
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testAdd() {
+        NavigableSet q = set0();
+        assertTrue(q.add(six));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testAddDup() {
+        NavigableSet q = set0();
+        assertTrue(q.add(six));
+        assertFalse(q.add(six));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testAddNonComparable() {
+        try {
+            NavigableSet q = set0();
+            q.add(new Object());
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        try {
+            NavigableSet q = set0();
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        try {
+            NavigableSet q = set0();
+            Integer[] ints = new Integer[SIZE];
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        try {
+            NavigableSet q = set0();
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i+SIZE);
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE-1- i);
+        NavigableSet q = set0();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.pollFirst());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i-1));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i+1));
+            assertFalse(q.contains(i+1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        NavigableSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = set0();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE-i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            NavigableSet q = populatedSet(SIZE);
+            NavigableSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE-i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer I = (Integer)(p.pollFirst());
+                assertFalse(q.contains(I));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testLower() {
+        NavigableSet q = set5();
+        Object e1 = q.lower(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lower(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lower(one);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testHigher() {
+        NavigableSet q = set5();
+        Object e1 = q.higher(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higher(five);
+        assertNull(e3);
+
+        Object e4 = q.higher(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testFloor() {
+        NavigableSet q = set5();
+        Object e1 = q.floor(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floor(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floor(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testCeiling() {
+        NavigableSet q = set5();
+        Object e1 = q.ceiling(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceiling(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceiling(six);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements in sorted order
+     */
+    public void testToArray() {
+        NavigableSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements in sorted order
+     */
+    public void testToArray2() {
+        NavigableSet<Integer> q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        NavigableSet q = populatedSet(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testEmptyIterator() {
+        NavigableSet q = set0();
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(0, i);
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final NavigableSet q = set0();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        NavigableSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testSerialization() throws Exception {
+        NavigableSet x = populatedSet(SIZE);
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testSubSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.subSet(two, four);
+        assertEquals(two, sm.first());
+        assertEquals(three, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.first());
+        assertEquals(three, sm.last());
+        assertTrue(sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testSubSetContents2() {
+        NavigableSet set = set5();
+        SortedSet sm = set.subSet(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.first());
+        assertEquals(two, sm.last());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertFalse(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(three));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testHeadSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.headSet(four);
+        assertTrue(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(four, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testTailSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.tailSet(two);
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertTrue(sm.contains(four));
+        assertTrue(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(four);
+        assertEquals(four, ssm.first());
+        assertEquals(five, ssm.last());
+        assertTrue(ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testDescendingSize() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testDescendingAddNull() {
+        try {
+            NavigableSet q = dset0();
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testDescendingAdd() {
+        NavigableSet q = dset0();
+        assertTrue(q.add(m6));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testDescendingAddDup() {
+        NavigableSet q = dset0();
+        assertTrue(q.add(m6));
+        assertFalse(q.add(m6));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testDescendingAddNonComparable() {
+        try {
+            NavigableSet q = dset0();
+            q.add(new Object());
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testDescendingAddAll1() {
+        try {
+            NavigableSet q = dset0();
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testDescendingAddAll2() {
+        try {
+            NavigableSet q = dset0();
+            Integer[] ints = new Integer[SIZE];
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testDescendingAddAll3() {
+        try {
+            NavigableSet q = dset0();
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i+SIZE);
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testDescendingAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE-1- i);
+        NavigableSet q = dset0();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.pollFirst());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testDescendingPoll() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testDescendingRemoveElement() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.remove(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.remove(new Integer(i)));
+            assertFalse(q.remove(new Integer(i+1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testDescendingContains() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testDescendingClear() {
+        NavigableSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testDescendingContainsAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = dset0();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testDescendingRetainAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE-i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testDescendingRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            NavigableSet q = populatedSet(SIZE);
+            NavigableSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE-i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer I = (Integer)(p.pollFirst());
+                assertFalse(q.contains(I));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testDescendingLower() {
+        NavigableSet q = dset5();
+        Object e1 = q.lower(m3);
+        assertEquals(m2, e1);
+
+        Object e2 = q.lower(m6);
+        assertEquals(m5, e2);
+
+        Object e3 = q.lower(m1);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testDescendingHigher() {
+        NavigableSet q = dset5();
+        Object e1 = q.higher(m3);
+        assertEquals(m4, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(m1, e2);
+
+        Object e3 = q.higher(m5);
+        assertNull(e3);
+
+        Object e4 = q.higher(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testDescendingFloor() {
+        NavigableSet q = dset5();
+        Object e1 = q.floor(m3);
+        assertEquals(m3, e1);
+
+        Object e2 = q.floor(m6);
+        assertEquals(m5, e2);
+
+        Object e3 = q.floor(m1);
+        assertEquals(m1, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testDescendingCeiling() {
+        NavigableSet q = dset5();
+        Object e1 = q.ceiling(m3);
+        assertEquals(m3, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(m1, e2);
+
+        Object e3 = q.ceiling(m5);
+        assertEquals(m5, e3);
+
+        Object e4 = q.ceiling(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements
+     */
+    public void testDescendingToArray() {
+        NavigableSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        Arrays.sort(o);
+        for (int i = 0; i < o.length; i++)
+            assertEquals(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements
+     */
+    public void testDescendingToArray2() {
+        NavigableSet q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        assertSame(ints, q.toArray(ints));
+        Arrays.sort(ints);
+        for (int i = 0; i < ints.length; i++)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        NavigableSet q = populatedSet(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testDescendingEmptyIterator() {
+        NavigableSet q = dset0();
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(0, i);
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final NavigableSet q = dset0();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testDescendingToString() {
+        NavigableSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testDescendingSerialization() throws Exception {
+        NavigableSet x = dset5();
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testDescendingSubSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.subSet(m2, m4);
+        assertEquals(m2, sm.first());
+        assertEquals(m3, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(m2));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(m3, sm.first());
+        assertEquals(m3, sm.last());
+        assertTrue(sm.remove(m3));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testDescendingSubSetContents2() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.subSet(m2, m3);
+        assertEquals(1, sm.size());
+        assertEquals(m2, sm.first());
+        assertEquals(m2, sm.last());
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertFalse(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(m2));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(m3));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testDescendingHeadSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.headSet(m4);
+        assertTrue(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m1, k);
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(m4, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testDescendingTailSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.tailSet(m2);
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertTrue(sm.contains(m4));
+        assertTrue(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        k = (Integer)(i.next());
+        assertEquals(m4, k);
+        k = (Integer)(i.next());
+        assertEquals(m5, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(m4);
+        assertEquals(m4, ssm.first());
+        assertEquals(m5, ssm.last());
+        assertTrue(ssm.remove(m4));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/CopyOnWriteArrayListTest.java b/jsr166-tests/src/test/java/jsr166/CopyOnWriteArrayListTest.java
new file mode 100644
index 0000000..6bef8be
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/CopyOnWriteArrayListTest.java
@@ -0,0 +1,705 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+import java.util.Vector;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public class CopyOnWriteArrayListTest extends JSR166TestCase {
+
+    static CopyOnWriteArrayList<Integer> populatedArray(int n) {
+        CopyOnWriteArrayList<Integer> a = new CopyOnWriteArrayList<Integer>();
+        assertTrue(a.isEmpty());
+        for (int i = 0; i < n; i++)
+            a.add(i);
+        assertFalse(a.isEmpty());
+        assertEquals(n, a.size());
+        return a;
+    }
+
+    static CopyOnWriteArrayList<Integer> populatedArray(Integer[] elements) {
+        CopyOnWriteArrayList<Integer> a = new CopyOnWriteArrayList<Integer>();
+        assertTrue(a.isEmpty());
+        for (int i = 0; i < elements.length; i++)
+            a.add(elements[i]);
+        assertFalse(a.isEmpty());
+        assertEquals(elements.length, a.size());
+        return a;
+    }
+
+    /**
+     * a new list is empty
+     */
+    public void testConstructor() {
+        CopyOnWriteArrayList a = new CopyOnWriteArrayList();
+        assertTrue(a.isEmpty());
+    }
+
+    /**
+     * new list contains all elements of initializing array
+     */
+    public void testConstructor2() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE-1; ++i)
+            ints[i] = new Integer(i);
+        CopyOnWriteArrayList a = new CopyOnWriteArrayList(ints);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], a.get(i));
+    }
+
+    /**
+     * new list contains all elements of initializing collection
+     */
+    public void testConstructor3() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE-1; ++i)
+            ints[i] = new Integer(i);
+        CopyOnWriteArrayList a = new CopyOnWriteArrayList(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], a.get(i));
+    }
+
+    /**
+     * addAll adds each element from the given collection
+     */
+    public void testAddAll() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        Vector v = new Vector();
+        v.add(three);
+        v.add(four);
+        v.add(five);
+        full.addAll(v);
+        assertEquals(6, full.size());
+    }
+
+    /**
+     * addAllAbsent adds each element from the given collection that did not
+     * already exist in the List
+     */
+    public void testAddAllAbsent() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        Vector v = new Vector();
+        v.add(three);
+        v.add(four);
+        v.add(one); // will not add this element
+        full.addAllAbsent(v);
+        assertEquals(5, full.size());
+    }
+
+    /**
+     * addIfAbsent will not add the element if it already exists in the list
+     */
+    public void testAddIfAbsent() {
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        full.addIfAbsent(one);
+        assertEquals(SIZE, full.size());
+    }
+
+    /**
+     * addIfAbsent adds the element when it does not exist in the list
+     */
+    public void testAddIfAbsent2() {
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        full.addIfAbsent(three);
+        assertTrue(full.contains(three));
+    }
+
+    /**
+     * clear removes all elements from the list
+     */
+    public void testClear() {
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        full.clear();
+        assertEquals(0, full.size());
+    }
+
+    /**
+     * Cloned list is equal
+     */
+    public void testClone() {
+        CopyOnWriteArrayList l1 = populatedArray(SIZE);
+        CopyOnWriteArrayList l2 = (CopyOnWriteArrayList)(l1.clone());
+        assertEquals(l1, l2);
+        l1.clear();
+        assertFalse(l1.equals(l2));
+    }
+
+    /**
+     * contains is true for added elements
+     */
+    public void testContains() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertTrue(full.contains(one));
+        assertFalse(full.contains(five));
+    }
+
+    /**
+     * adding at an index places it in the indicated index
+     */
+    public void testAddIndex() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        full.add(0, m1);
+        assertEquals(4, full.size());
+        assertEquals(m1, full.get(0));
+        assertEquals(zero, full.get(1));
+
+        full.add(2, m2);
+        assertEquals(5, full.size());
+        assertEquals(m2, full.get(2));
+        assertEquals(two, full.get(4));
+    }
+
+    /**
+     * lists with same elements are equal and have same hashCode
+     */
+    public void testEquals() {
+        CopyOnWriteArrayList a = populatedArray(3);
+        CopyOnWriteArrayList b = populatedArray(3);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertEquals(a.hashCode(), b.hashCode());
+        a.add(m1);
+        assertFalse(a.equals(b));
+        assertFalse(b.equals(a));
+        b.add(m1);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertEquals(a.hashCode(), b.hashCode());
+    }
+
+    /**
+     * containsAll returns true for collection with subset of elements
+     */
+    public void testContainsAll() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        Vector v = new Vector();
+        v.add(one);
+        v.add(two);
+        assertTrue(full.containsAll(v));
+        v.add(six);
+        assertFalse(full.containsAll(v));
+    }
+
+    /**
+     * get returns the value at the given index
+     */
+    public void testGet() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertEquals(0, full.get(0));
+    }
+
+    /**
+     * indexOf gives the index for the given object
+     */
+    public void testIndexOf() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertEquals(1, full.indexOf(one));
+        assertEquals(-1, full.indexOf("puppies"));
+    }
+
+    /**
+     * indexOf gives the index based on the given index
+     * at which to start searching
+     */
+    public void testIndexOf2() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertEquals(1, full.indexOf(one, 0));
+        assertEquals(-1, full.indexOf(one, 2));
+    }
+
+    /**
+     * isEmpty returns true when empty, else false
+     */
+    public void testIsEmpty() {
+        CopyOnWriteArrayList empty = new CopyOnWriteArrayList();
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        assertTrue(empty.isEmpty());
+        assertFalse(full.isEmpty());
+    }
+
+    /**
+     * iterator() returns an iterator containing the elements of the
+     * list in insertion order
+     */
+    public void testIterator() {
+        Collection empty = new CopyOnWriteArrayList();
+        assertFalse(empty.iterator().hasNext());
+        try {
+            empty.iterator().next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        Collections.shuffle(Arrays.asList(elements));
+        Collection<Integer> full = populatedArray(elements);
+
+        Iterator it = full.iterator();
+        for (int j = 0; j < SIZE; j++) {
+            assertTrue(it.hasNext());
+            assertEquals(elements[j], it.next());
+        }
+        assertFalse(it.hasNext());
+        try {
+            it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * iterator.remove throws UnsupportedOperationException
+     */
+    public void testIteratorRemove() {
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        Iterator it = full.iterator();
+        it.next();
+        try {
+            it.remove();
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        assertEquals("[]", new CopyOnWriteArrayList().toString());
+        CopyOnWriteArrayList full = populatedArray(3);
+        String s = full.toString();
+        for (int i = 0; i < 3; ++i)
+            assertTrue(s.contains(String.valueOf(i)));
+        assertEquals(new ArrayList(full).toString(),
+                     full.toString());
+    }
+
+    /**
+     * lastIndexOf returns the index for the given object
+     */
+    public void testLastIndexOf1() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        full.add(one);
+        full.add(three);
+        assertEquals(3, full.lastIndexOf(one));
+        assertEquals(-1, full.lastIndexOf(six));
+    }
+
+    /**
+     * lastIndexOf returns the index from the given starting point
+     */
+    public void testLastIndexOf2() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        full.add(one);
+        full.add(three);
+        assertEquals(3, full.lastIndexOf(one, 4));
+        assertEquals(-1, full.lastIndexOf(three, 3));
+    }
+
+    /**
+     * listIterator traverses all elements
+     */
+    public void testListIterator1() {
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        ListIterator i = full.listIterator();
+        int j;
+        for (j = 0; i.hasNext(); j++)
+            assertEquals(j, i.next());
+        assertEquals(SIZE, j);
+    }
+
+    /**
+     * listIterator only returns those elements after the given index
+     */
+    public void testListIterator2() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        ListIterator i = full.listIterator(1);
+        int j;
+        for (j = 0; i.hasNext(); j++)
+            assertEquals(j+1, i.next());
+        assertEquals(2, j);
+    }
+
+    /**
+     * remove(int) removes and returns the object at the given index
+     */
+    public void testRemove_int() {
+        int SIZE = 3;
+        for (int i = 0; i < SIZE; i++) {
+            CopyOnWriteArrayList full = populatedArray(SIZE);
+            assertEquals(i, full.remove(i));
+            assertEquals(SIZE - 1, full.size());
+            assertFalse(full.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * remove(Object) removes the object if found and returns true
+     */
+    public void testRemove_Object() {
+        int SIZE = 3;
+        for (int i = 0; i < SIZE; i++) {
+            CopyOnWriteArrayList full = populatedArray(SIZE);
+            assertFalse(full.remove(new Integer(-42)));
+            assertTrue(full.remove(new Integer(i)));
+            assertEquals(SIZE - 1, full.size());
+            assertFalse(full.contains(new Integer(i)));
+        }
+        CopyOnWriteArrayList x = new CopyOnWriteArrayList(Arrays.asList(4, 5, 6));
+        assertTrue(x.remove(new Integer(6)));
+        assertEquals(x, Arrays.asList(4, 5));
+        assertTrue(x.remove(new Integer(4)));
+        assertEquals(x, Arrays.asList(5));
+        assertTrue(x.remove(new Integer(5)));
+        assertEquals(x, Arrays.asList());
+        assertFalse(x.remove(new Integer(5)));
+    }
+
+    /**
+     * removeAll removes all elements from the given collection
+     */
+    public void testRemoveAll() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        Vector v = new Vector();
+        v.add(one);
+        v.add(two);
+        full.removeAll(v);
+        assertEquals(1, full.size());
+    }
+
+    /**
+     * set changes the element at the given index
+     */
+    public void testSet() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertEquals(2, full.set(2, four));
+        assertEquals(4, full.get(2));
+    }
+
+    /**
+     * size returns the number of elements
+     */
+    public void testSize() {
+        CopyOnWriteArrayList empty = new CopyOnWriteArrayList();
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        assertEquals(SIZE, full.size());
+        assertEquals(0, empty.size());
+    }
+
+    /**
+     * toArray() returns an Object array containing all elements from
+     * the list in insertion order
+     */
+    public void testToArray() {
+        Object[] a = new CopyOnWriteArrayList().toArray();
+        assertTrue(Arrays.equals(new Object[0], a));
+        assertSame(Object[].class, a.getClass());
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        Collections.shuffle(Arrays.asList(elements));
+        Collection<Integer> full = populatedArray(elements);
+
+        assertTrue(Arrays.equals(elements, full.toArray()));
+        assertSame(Object[].class, full.toArray().getClass());
+    }
+
+    /**
+     * toArray(Integer array) returns an Integer array containing all
+     * elements from the list in insertion order
+     */
+    public void testToArray2() {
+        Collection empty = new CopyOnWriteArrayList();
+        Integer[] a;
+
+        a = new Integer[0];
+        assertSame(a, empty.toArray(a));
+
+        a = new Integer[SIZE/2];
+        Arrays.fill(a, 42);
+        assertSame(a, empty.toArray(a));
+        assertNull(a[0]);
+        for (int i = 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        Collections.shuffle(Arrays.asList(elements));
+        Collection<Integer> full = populatedArray(elements);
+
+        Arrays.fill(a, 42);
+        assertTrue(Arrays.equals(elements, full.toArray(a)));
+        for (int i = 0; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+        assertSame(Integer[].class, full.toArray(a).getClass());
+
+        a = new Integer[SIZE];
+        Arrays.fill(a, 42);
+        assertSame(a, full.toArray(a));
+        assertTrue(Arrays.equals(elements, a));
+
+        a = new Integer[2*SIZE];
+        Arrays.fill(a, 42);
+        assertSame(a, full.toArray(a));
+        assertTrue(Arrays.equals(elements, Arrays.copyOf(a, SIZE)));
+        assertNull(a[SIZE]);
+        for (int i = SIZE + 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+    }
+
+    /**
+     * sublists contains elements at indexes offset from their base
+     */
+    public void testSubList() {
+        CopyOnWriteArrayList a = populatedArray(10);
+        assertTrue(a.subList(1,1).isEmpty());
+        for (int j = 0; j < 9; ++j) {
+            for (int i = j ; i < 10; ++i) {
+                List b = a.subList(j,i);
+                for (int k = j; k < i; ++k) {
+                    assertEquals(new Integer(k), b.get(k-j));
+                }
+            }
+        }
+
+        List s = a.subList(2, 5);
+        assertEquals(3, s.size());
+        s.set(2, m1);
+        assertEquals(a.get(4), m1);
+        s.clear();
+        assertEquals(7, a.size());
+    }
+
+    // Exception tests
+
+    /**
+     * toArray throws an ArrayStoreException when the given array
+     * can not store the objects inside the list
+     */
+    public void testToArray_ArrayStoreException() {
+        try {
+            CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+            c.add("zfasdfsdf");
+            c.add("asdadasd");
+            c.toArray(new Long[5]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * get throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testGet1_IndexOutOfBoundsException() {
+        try {
+            CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+            c.get(-1);
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * get throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testGet2_IndexOutOfBoundsException() {
+        try {
+            CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+            c.add("asdasd");
+            c.add("asdad");
+            c.get(100);
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * set throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testSet1_IndexOutOfBoundsException() {
+        try {
+            CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+            c.set(-1,"qwerty");
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * set throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testSet2() {
+        try {
+            CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+            c.add("asdasd");
+            c.add("asdad");
+            c.set(100, "qwerty");
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * add throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testAdd1_IndexOutOfBoundsException() {
+        try {
+            CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+            c.add(-1,"qwerty");
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * add throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testAdd2_IndexOutOfBoundsException() {
+        try {
+            CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+            c.add("asdasd");
+            c.add("asdasdasd");
+            c.add(100, "qwerty");
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * remove throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testRemove1_IndexOutOfBounds() {
+        try {
+            CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+            c.remove(-1);
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * remove throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testRemove2_IndexOutOfBounds() {
+        try {
+            CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+            c.add("asdasd");
+            c.add("adasdasd");
+            c.remove(100);
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * addAll throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testAddAll1_IndexOutOfBoundsException() {
+        try {
+            CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+            c.addAll(-1,new LinkedList());
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * addAll throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testAddAll2_IndexOutOfBoundsException() {
+        try {
+            CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+            c.add("asdasd");
+            c.add("asdasdasd");
+            c.addAll(100, new LinkedList());
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * listIterator throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testListIterator1_IndexOutOfBoundsException() {
+        try {
+            CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+            c.listIterator(-1);
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * listIterator throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testListIterator2_IndexOutOfBoundsException() {
+        try {
+            CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+            c.add("adasd");
+            c.add("asdasdas");
+            c.listIterator(100);
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * subList throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testSubList1_IndexOutOfBoundsException() {
+        try {
+            CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+            c.subList(-1,100);
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * subList throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testSubList2_IndexOutOfBoundsException() {
+        try {
+            CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+            c.add("asdasd");
+            c.subList(1,100);
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * subList throws IndexOutOfBoundsException when the second index
+     * is lower then the first
+     */
+    public void testSubList3_IndexOutOfBoundsException() {
+        try {
+            CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+            c.subList(3,1);
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * a deserialized serialized list is equal
+     */
+    public void testSerialization() throws Exception {
+        List x = populatedArray(SIZE);
+        List y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(0), y.remove(0));
+        }
+        assertTrue(y.isEmpty());
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/CopyOnWriteArraySetTest.java b/jsr166-tests/src/test/java/jsr166/CopyOnWriteArraySetTest.java
new file mode 100644
index 0000000..feb283f
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/CopyOnWriteArraySetTest.java
@@ -0,0 +1,359 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.Vector;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+public class CopyOnWriteArraySetTest extends JSR166TestCase {
+
+    static CopyOnWriteArraySet<Integer> populatedSet(int n) {
+        CopyOnWriteArraySet<Integer> a = new CopyOnWriteArraySet<Integer>();
+        assertTrue(a.isEmpty());
+        for (int i = 0; i < n; i++)
+            a.add(i);
+        assertFalse(a.isEmpty());
+        assertEquals(n, a.size());
+        return a;
+    }
+
+    static CopyOnWriteArraySet populatedSet(Integer[] elements) {
+        CopyOnWriteArraySet<Integer> a = new CopyOnWriteArraySet<Integer>();
+        assertTrue(a.isEmpty());
+        for (int i = 0; i < elements.length; i++)
+            a.add(elements[i]);
+        assertFalse(a.isEmpty());
+        assertEquals(elements.length, a.size());
+        return a;
+    }
+
+    /**
+     * Default-constructed set is empty
+     */
+    public void testConstructor() {
+        CopyOnWriteArraySet a = new CopyOnWriteArraySet();
+        assertTrue(a.isEmpty());
+    }
+
+    /**
+     * Collection-constructed set holds all of its elements
+     */
+    public void testConstructor3() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE-1; ++i)
+            ints[i] = new Integer(i);
+        CopyOnWriteArraySet a = new CopyOnWriteArraySet(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertTrue(a.contains(ints[i]));
+    }
+
+    /**
+     * addAll adds each element from the given collection
+     */
+    public void testAddAll() {
+        CopyOnWriteArraySet full = populatedSet(3);
+        Vector v = new Vector();
+        v.add(three);
+        v.add(four);
+        v.add(five);
+        full.addAll(v);
+        assertEquals(6, full.size());
+    }
+
+    /**
+     * addAll adds each element from the given collection that did not
+     * already exist in the set
+     */
+    public void testAddAll2() {
+        CopyOnWriteArraySet full = populatedSet(3);
+        Vector v = new Vector();
+        v.add(three);
+        v.add(four);
+        v.add(one); // will not add this element
+        full.addAll(v);
+        assertEquals(5, full.size());
+    }
+
+    /**
+     * add will not add the element if it already exists in the set
+     */
+    public void testAdd2() {
+        CopyOnWriteArraySet full = populatedSet(3);
+        full.add(one);
+        assertEquals(3, full.size());
+    }
+
+    /**
+     * add adds the element when it does not exist in the set
+     */
+    public void testAdd3() {
+        CopyOnWriteArraySet full = populatedSet(3);
+        full.add(three);
+        assertTrue(full.contains(three));
+    }
+
+    /**
+     * clear removes all elements from the set
+     */
+    public void testClear() {
+        CopyOnWriteArraySet full = populatedSet(3);
+        full.clear();
+        assertEquals(0, full.size());
+    }
+
+    /**
+     * contains returns true for added elements
+     */
+    public void testContains() {
+        CopyOnWriteArraySet full = populatedSet(3);
+        assertTrue(full.contains(one));
+        assertFalse(full.contains(five));
+    }
+
+    /**
+     * Sets with equal elements are equal
+     */
+    public void testEquals() {
+        CopyOnWriteArraySet a = populatedSet(3);
+        CopyOnWriteArraySet b = populatedSet(3);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertEquals(a.hashCode(), b.hashCode());
+        a.add(m1);
+        assertFalse(a.equals(b));
+        assertFalse(b.equals(a));
+        b.add(m1);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertEquals(a.hashCode(), b.hashCode());
+    }
+
+    /**
+     * containsAll returns true for collections with subset of elements
+     */
+    public void testContainsAll() {
+        CopyOnWriteArraySet full = populatedSet(3);
+        Vector v = new Vector();
+        v.add(one);
+        v.add(two);
+        assertTrue(full.containsAll(v));
+        v.add(six);
+        assertFalse(full.containsAll(v));
+    }
+
+    /**
+     * isEmpty is true when empty, else false
+     */
+    public void testIsEmpty() {
+        CopyOnWriteArraySet empty = new CopyOnWriteArraySet();
+        CopyOnWriteArraySet full = populatedSet(3);
+        assertTrue(empty.isEmpty());
+        assertFalse(full.isEmpty());
+    }
+
+    /**
+     * iterator() returns an iterator containing the elements of the
+     * set in insertion order
+     */
+    public void testIterator() {
+        Collection empty = new CopyOnWriteArraySet();
+        assertFalse(empty.iterator().hasNext());
+        try {
+            empty.iterator().next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        Collections.shuffle(Arrays.asList(elements));
+        Collection<Integer> full = populatedSet(elements);
+
+        Iterator it = full.iterator();
+        for (int j = 0; j < SIZE; j++) {
+            assertTrue(it.hasNext());
+            assertEquals(elements[j], it.next());
+        }
+        assertFalse(it.hasNext());
+        try {
+            it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * iterator remove is unsupported
+     */
+    public void testIteratorRemove() {
+        CopyOnWriteArraySet full = populatedSet(3);
+        Iterator it = full.iterator();
+        it.next();
+        try {
+            it.remove();
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+
+    /**
+     * toString holds toString of elements
+     */
+    public void testToString() {
+        assertEquals("[]", new CopyOnWriteArraySet().toString());
+        CopyOnWriteArraySet full = populatedSet(3);
+        String s = full.toString();
+        for (int i = 0; i < 3; ++i)
+            assertTrue(s.contains(String.valueOf(i)));
+        assertEquals(new ArrayList(full).toString(),
+                     full.toString());
+    }
+
+    /**
+     * removeAll removes all elements from the given collection
+     */
+    public void testRemoveAll() {
+        CopyOnWriteArraySet full = populatedSet(3);
+        Vector v = new Vector();
+        v.add(one);
+        v.add(two);
+        full.removeAll(v);
+        assertEquals(1, full.size());
+    }
+
+    /**
+     * remove removes an element
+     */
+    public void testRemove() {
+        CopyOnWriteArraySet full = populatedSet(3);
+        full.remove(one);
+        assertFalse(full.contains(one));
+        assertEquals(2, full.size());
+    }
+
+    /**
+     * size returns the number of elements
+     */
+    public void testSize() {
+        CopyOnWriteArraySet empty = new CopyOnWriteArraySet();
+        CopyOnWriteArraySet full = populatedSet(3);
+        assertEquals(3, full.size());
+        assertEquals(0, empty.size());
+    }
+
+    /**
+     * toArray() returns an Object array containing all elements from
+     * the set in insertion order
+     */
+    public void testToArray() {
+        Object[] a = new CopyOnWriteArraySet().toArray();
+        assertTrue(Arrays.equals(new Object[0], a));
+        assertSame(Object[].class, a.getClass());
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        Collections.shuffle(Arrays.asList(elements));
+        Collection<Integer> full = populatedSet(elements);
+
+        assertTrue(Arrays.equals(elements, full.toArray()));
+        assertSame(Object[].class, full.toArray().getClass());
+    }
+
+    /**
+     * toArray(Integer array) returns an Integer array containing all
+     * elements from the set in insertion order
+     */
+    public void testToArray2() {
+        Collection empty = new CopyOnWriteArraySet();
+        Integer[] a;
+
+        a = new Integer[0];
+        assertSame(a, empty.toArray(a));
+
+        a = new Integer[SIZE/2];
+        Arrays.fill(a, 42);
+        assertSame(a, empty.toArray(a));
+        assertNull(a[0]);
+        for (int i = 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        Collections.shuffle(Arrays.asList(elements));
+        Collection<Integer> full = populatedSet(elements);
+
+        Arrays.fill(a, 42);
+        assertTrue(Arrays.equals(elements, full.toArray(a)));
+        for (int i = 0; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+        assertSame(Integer[].class, full.toArray(a).getClass());
+
+        a = new Integer[SIZE];
+        Arrays.fill(a, 42);
+        assertSame(a, full.toArray(a));
+        assertTrue(Arrays.equals(elements, a));
+
+        a = new Integer[2*SIZE];
+        Arrays.fill(a, 42);
+        assertSame(a, full.toArray(a));
+        assertTrue(Arrays.equals(elements, Arrays.copyOf(a, SIZE)));
+        assertNull(a[SIZE]);
+        for (int i = SIZE + 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+    }
+
+    /**
+     * toArray throws an ArrayStoreException when the given array can
+     * not store the objects inside the set
+     */
+    public void testToArray_ArrayStoreException() {
+        try {
+            CopyOnWriteArraySet c = new CopyOnWriteArraySet();
+            c.add("zfasdfsdf");
+            c.add("asdadasd");
+            c.toArray(new Long[5]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * A deserialized serialized set is equal
+     */
+    public void testSerialization() throws Exception {
+        Set x = populatedSet(SIZE);
+        Set y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * addAll is idempotent
+     */
+    public void testAddAll_idempotent() throws Exception {
+        Set x = populatedSet(SIZE);
+        Set y = new CopyOnWriteArraySet(x);
+        y.addAll(x);
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/CountDownLatchTest.java b/jsr166-tests/src/test/java/jsr166/CountDownLatchTest.java
new file mode 100644
index 0000000..bc2aecf
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/CountDownLatchTest.java
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+import java.util.concurrent.CountDownLatch;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class CountDownLatchTest extends JSR166TestCase {
+
+    /**
+     * negative constructor argument throws IAE
+     */
+    public void testConstructor() {
+        try {
+            new CountDownLatch(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getCount returns initial count and decreases after countDown
+     */
+    public void testGetCount() {
+        final CountDownLatch l = new CountDownLatch(2);
+        assertEquals(2, l.getCount());
+        l.countDown();
+        assertEquals(1, l.getCount());
+    }
+
+    /**
+     * countDown decrements count when positive and has no effect when zero
+     */
+    public void testCountDown() {
+        final CountDownLatch l = new CountDownLatch(1);
+        assertEquals(1, l.getCount());
+        l.countDown();
+        assertEquals(0, l.getCount());
+        l.countDown();
+        assertEquals(0, l.getCount());
+    }
+
+    /**
+     * await returns after countDown to zero, but not before
+     */
+    public void testAwait() {
+        final CountDownLatch l = new CountDownLatch(2);
+        final CountDownLatch pleaseCountDown = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertEquals(2, l.getCount());
+                pleaseCountDown.countDown();
+                l.await();
+                assertEquals(0, l.getCount());
+            }});
+
+        await(pleaseCountDown);
+        assertEquals(2, l.getCount());
+        l.countDown();
+        assertEquals(1, l.getCount());
+        assertThreadStaysAlive(t);
+        l.countDown();
+        assertEquals(0, l.getCount());
+        awaitTermination(t);
+    }
+
+    /**
+     * timed await returns after countDown to zero
+     */
+    public void testTimedAwait() {
+        final CountDownLatch l = new CountDownLatch(2);
+        final CountDownLatch pleaseCountDown = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertEquals(2, l.getCount());
+                pleaseCountDown.countDown();
+                assertTrue(l.await(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(0, l.getCount());
+            }});
+
+        await(pleaseCountDown);
+        assertEquals(2, l.getCount());
+        l.countDown();
+        assertEquals(1, l.getCount());
+        assertThreadStaysAlive(t);
+        l.countDown();
+        assertEquals(0, l.getCount());
+        awaitTermination(t);
+    }
+
+    /**
+     * await throws IE if interrupted before counted down
+     */
+    public void testAwait_Interruptible() {
+        final CountDownLatch l = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Thread.currentThread().interrupt();
+                try {
+                    l.await();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    l.await();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                assertEquals(1, l.getCount());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed await throws IE if interrupted before counted down
+     */
+    public void testTimedAwait_Interruptible() {
+        final CountDownLatch l = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Thread.currentThread().interrupt();
+                try {
+                    l.await(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    l.await(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                assertEquals(1, l.getCount());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed await times out if not counted down before timeout
+     */
+    public void testAwaitTimeout() throws InterruptedException {
+        final CountDownLatch l = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertEquals(1, l.getCount());
+                assertFalse(l.await(timeoutMillis(), MILLISECONDS));
+                assertEquals(1, l.getCount());
+            }});
+
+        awaitTermination(t);
+        assertEquals(1, l.getCount());
+    }
+
+    /**
+     * toString indicates current count
+     */
+    public void testToString() {
+        CountDownLatch s = new CountDownLatch(2);
+        assertTrue(s.toString().contains("Count = 2"));
+        s.countDown();
+        assertTrue(s.toString().contains("Count = 1"));
+        s.countDown();
+        assertTrue(s.toString().contains("Count = 0"));
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/CountedCompleterTest.java b/jsr166-tests/src/test/java/jsr166/CountedCompleterTest.java
new file mode 100644
index 0000000..2f8665b
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/CountedCompleterTest.java
@@ -0,0 +1,1821 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.CountedCompleter;
+import java.util.concurrent.ForkJoinWorkerThread;
+import java.util.concurrent.RecursiveAction;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.atomic.AtomicReference;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import java.util.HashSet;
+import junit.framework.*;
+
+public class CountedCompleterTest extends JSR166TestCase {
+
+    // Runs with "mainPool" use > 1 thread. singletonPool tests use 1
+    static final int mainPoolSize =
+        Math.max(2, Runtime.getRuntime().availableProcessors());
+
+    private static ForkJoinPool mainPool() {
+        return new ForkJoinPool(mainPoolSize);
+    }
+
+    private static ForkJoinPool singletonPool() {
+        return new ForkJoinPool(1);
+    }
+
+    private static ForkJoinPool asyncSingletonPool() {
+        return new ForkJoinPool(1,
+                                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                                null, true);
+    }
+
+    private void testInvokeOnPool(ForkJoinPool pool, ForkJoinTask a) {
+        try {
+            assertFalse(a.isDone());
+            assertFalse(a.isCompletedNormally());
+            assertFalse(a.isCompletedAbnormally());
+            assertFalse(a.isCancelled());
+            assertNull(a.getException());
+            assertNull(a.getRawResult());
+
+            assertNull(pool.invoke(a));
+
+            assertTrue(a.isDone());
+            assertTrue(a.isCompletedNormally());
+            assertFalse(a.isCompletedAbnormally());
+            assertFalse(a.isCancelled());
+            assertNull(a.getException());
+            assertNull(a.getRawResult());
+        } finally {
+            joinPool(pool);
+        }
+    }
+
+    void checkNotDone(CountedCompleter a) {
+        assertFalse(a.isDone());
+        assertFalse(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertFalse(a.isCancelled());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        try {
+            a.get(0L, SECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedNormally(CountedCompleter<?> a) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertTrue(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        {
+            Thread.currentThread().interrupt();
+            long t0 = System.nanoTime();
+            assertNull(a.join());
+            assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+            Thread.interrupted();
+        }
+
+        {
+            Thread.currentThread().interrupt();
+            long t0 = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+            Thread.interrupted();
+        }
+
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        try {
+            assertNull(a.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertNull(a.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCancelled(CountedCompleter a) {
+        assertTrue(a.isDone());
+        assertTrue(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertTrue(a.getException() instanceof CancellationException);
+        assertNull(a.getRawResult());
+        assertTrue(a.cancel(false));
+        assertTrue(a.cancel(true));
+
+        try {
+            Thread.currentThread().interrupt();
+            a.join();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        Thread.interrupted();
+
+        {
+            long t0 = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedAbnormally(CountedCompleter a, Throwable t) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertSame(t.getClass(), a.getException().getClass());
+        assertNull(a.getRawResult());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+
+        try {
+            Thread.currentThread().interrupt();
+            a.join();
+            shouldThrow();
+        } catch (Throwable expected) {
+            assertSame(t.getClass(), expected.getClass());
+        }
+        Thread.interrupted();
+
+        {
+            long t0 = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.invoke();
+            shouldThrow();
+        } catch (Throwable ex) {
+            assertSame(t, ex);
+        }
+    }
+
+    public static final class FJException extends RuntimeException {
+        FJException() { super(); }
+    }
+
+    abstract class CheckedCC extends CountedCompleter<Object> {
+        final AtomicInteger computeN = new AtomicInteger(0);
+        final AtomicInteger onCompletionN = new AtomicInteger(0);
+        final AtomicInteger onExceptionalCompletionN = new AtomicInteger(0);
+        final AtomicInteger setRawResultN = new AtomicInteger(0);
+        final AtomicReference<Object> rawResult = new AtomicReference<Object>(null);
+        int computeN() { return computeN.get(); }
+        int onCompletionN() { return onCompletionN.get(); }
+        int onExceptionalCompletionN() { return onExceptionalCompletionN.get(); }
+        int setRawResultN() { return setRawResultN.get(); }
+
+        CheckedCC() { super(); }
+        CheckedCC(CountedCompleter p) { super(p); }
+        CheckedCC(CountedCompleter p, int n) { super(p, n); }
+        abstract void realCompute();
+        public final void compute() {
+            computeN.incrementAndGet();
+            realCompute();
+        }
+        public void onCompletion(CountedCompleter caller) {
+            onCompletionN.incrementAndGet();
+            super.onCompletion(caller);
+        }
+        public boolean onExceptionalCompletion(Throwable ex,
+                                               CountedCompleter caller) {
+            onExceptionalCompletionN.incrementAndGet();
+            assertNotNull(ex);
+            assertTrue(isCompletedAbnormally());
+            assertTrue(super.onExceptionalCompletion(ex, caller));
+            return true;
+        }
+        protected void setRawResult(Object t) {
+            setRawResultN.incrementAndGet();
+            rawResult.set(t);
+            super.setRawResult(t);
+        }
+        void checkIncomplete() {
+            assertEquals(0, computeN());
+            assertEquals(0, onCompletionN());
+            assertEquals(0, onExceptionalCompletionN());
+            assertEquals(0, setRawResultN());
+            checkNotDone(this);
+        }
+        void checkCompletes(Object rawResult) {
+            checkIncomplete();
+            int pendingCount = getPendingCount();
+            complete(rawResult);
+            assertEquals(pendingCount, getPendingCount());
+            assertEquals(0, computeN());
+            assertEquals(1, onCompletionN());
+            assertEquals(0, onExceptionalCompletionN());
+            assertEquals(1, setRawResultN());
+            assertSame(rawResult, this.rawResult.get());
+            checkCompletedNormally(this);
+        }
+        void checkCompletesExceptionally(Throwable ex) {
+            checkIncomplete();
+            completeExceptionally(ex);
+            checkCompletedExceptionally(ex);
+        }
+        void checkCompletedExceptionally(Throwable ex) {
+            assertEquals(0, computeN());
+            assertEquals(0, onCompletionN());
+            assertEquals(1, onExceptionalCompletionN());
+            assertEquals(0, setRawResultN());
+            assertNull(this.rawResult.get());
+            checkCompletedAbnormally(this, ex);
+        }
+    }
+
+    final class NoopCC extends CheckedCC {
+        NoopCC() { super(); }
+        NoopCC(CountedCompleter p) { super(p); }
+        protected void realCompute() {}
+    }
+
+    /**
+     * A newly constructed CountedCompleter is not completed;
+     * complete() causes completion. pendingCount is ignored.
+     */
+    public void testComplete() {
+        for (Object x : new Object[] { Boolean.TRUE, null }) {
+            for (int pendingCount : new int[] { 0, 42 }) {
+                testComplete(new NoopCC(), x, pendingCount);
+                testComplete(new NoopCC(new NoopCC()), x, pendingCount);
+            }
+        }
+    }
+    void testComplete(NoopCC cc, Object x, int pendingCount) {
+        cc.setPendingCount(pendingCount);
+        cc.checkCompletes(x);
+    }
+
+    /**
+     * completeExceptionally completes exceptionally
+     */
+    public void testCompleteExceptionally() {
+        new NoopCC()
+            .checkCompletesExceptionally(new FJException());
+        new NoopCC(new NoopCC())
+            .checkCompletesExceptionally(new FJException());
+    }
+
+    /**
+     * completeExceptionally(null) throws NullPointerException
+     */
+    public void testCompleteExceptionally_null() {
+        try {
+            new NoopCC()
+                .checkCompletesExceptionally(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * setPendingCount sets the reported pending count
+     */
+    public void testSetPendingCount() {
+        NoopCC a = new NoopCC();
+        assertEquals(0, a.getPendingCount());
+        a.setPendingCount(1);
+        assertEquals(1, a.getPendingCount());
+        a.setPendingCount(27);
+        assertEquals(27, a.getPendingCount());
+    }
+
+    /**
+     * addToPendingCount adds to the reported pending count
+     */
+    public void testAddToPendingCount() {
+        NoopCC a = new NoopCC();
+        assertEquals(0, a.getPendingCount());
+        a.addToPendingCount(1);
+        assertEquals(1, a.getPendingCount());
+        a.addToPendingCount(27);
+        assertEquals(28, a.getPendingCount());
+    }
+
+    /**
+     * decrementPendingCountUnlessZero decrements reported pending
+     * count unless zero
+     */
+    public void testDecrementPendingCount() {
+        NoopCC a = new NoopCC();
+        assertEquals(0, a.getPendingCount());
+        a.addToPendingCount(1);
+        assertEquals(1, a.getPendingCount());
+        a.decrementPendingCountUnlessZero();
+        assertEquals(0, a.getPendingCount());
+        a.decrementPendingCountUnlessZero();
+        assertEquals(0, a.getPendingCount());
+    }
+
+    /**
+     * compareAndSetPendingCount compares and sets the reported
+     * pending count
+     */
+    public void testCompareAndSetPendingCount() {
+        NoopCC a = new NoopCC();
+        assertEquals(0, a.getPendingCount());
+        assertTrue(a.compareAndSetPendingCount(0, 1));
+        assertEquals(1, a.getPendingCount());
+        assertTrue(a.compareAndSetPendingCount(1, 2));
+        assertEquals(2, a.getPendingCount());
+        assertFalse(a.compareAndSetPendingCount(1, 3));
+        assertEquals(2, a.getPendingCount());
+    }
+
+    /**
+     * getCompleter returns parent or null if at root
+     */
+    public void testGetCompleter() {
+        NoopCC a = new NoopCC();
+        assertNull(a.getCompleter());
+        CountedCompleter b = new NoopCC(a);
+        assertSame(a, b.getCompleter());
+        CountedCompleter c = new NoopCC(b);
+        assertSame(b, c.getCompleter());
+    }
+
+    /**
+     * getRoot returns self if no parent, else parent's root
+     */
+    public void testGetRoot() {
+        NoopCC a = new NoopCC();
+        NoopCC b = new NoopCC(a);
+        NoopCC c = new NoopCC(b);
+        assertSame(a, a.getRoot());
+        assertSame(a, b.getRoot());
+        assertSame(a, c.getRoot());
+    }
+
+    /**
+     * tryComplete decrements pending count unless zero, in which case
+     * causes completion
+     */
+    public void testTryComplete() {
+        NoopCC a = new NoopCC();
+        assertEquals(0, a.getPendingCount());
+        int n = 3;
+        a.setPendingCount(n);
+        for (; n > 0; n--) {
+            assertEquals(n, a.getPendingCount());
+            a.tryComplete();
+            a.checkIncomplete();
+            assertEquals(n - 1, a.getPendingCount());
+        }
+        a.tryComplete();
+        assertEquals(0, a.computeN());
+        assertEquals(1, a.onCompletionN());
+        assertEquals(0, a.onExceptionalCompletionN());
+        assertEquals(0, a.setRawResultN());
+        checkCompletedNormally(a);
+    }
+
+    /**
+     * propagateCompletion decrements pending count unless zero, in
+     * which case causes completion, without invoking onCompletion
+     */
+    public void testPropagateCompletion() {
+        NoopCC a = new NoopCC();
+        assertEquals(0, a.getPendingCount());
+        int n = 3;
+        a.setPendingCount(n);
+        for (; n > 0; n--) {
+            assertEquals(n, a.getPendingCount());
+            a.propagateCompletion();
+            a.checkIncomplete();
+            assertEquals(n - 1, a.getPendingCount());
+        }
+        a.propagateCompletion();
+        assertEquals(0, a.computeN());
+        assertEquals(0, a.onCompletionN());
+        assertEquals(0, a.onExceptionalCompletionN());
+        assertEquals(0, a.setRawResultN());
+        checkCompletedNormally(a);
+    }
+
+    /**
+     * firstComplete returns this if pending count is zero else null
+     */
+    public void testFirstComplete() {
+        NoopCC a = new NoopCC();
+        a.setPendingCount(1);
+        assertNull(a.firstComplete());
+        a.checkIncomplete();
+        assertSame(a, a.firstComplete());
+        a.checkIncomplete();
+    }
+
+    /**
+     * firstComplete.nextComplete returns parent if pending count is
+     * zero else null
+     */
+    public void testNextComplete() {
+        NoopCC a = new NoopCC();
+        NoopCC b = new NoopCC(a);
+        a.setPendingCount(1);
+        b.setPendingCount(1);
+        assertNull(b.firstComplete());
+        assertSame(b, b.firstComplete());
+        assertNull(b.nextComplete());
+        a.checkIncomplete();
+        b.checkIncomplete();
+        assertSame(a, b.nextComplete());
+        assertSame(a, b.nextComplete());
+        a.checkIncomplete();
+        b.checkIncomplete();
+        assertNull(a.nextComplete());
+        b.checkIncomplete();
+        checkCompletedNormally(a);
+    }
+
+    /**
+     * quietlyCompleteRoot completes root task
+     */
+    public void testQuietlyCompleteRoot() {
+        NoopCC a = new NoopCC();
+        NoopCC b = new NoopCC(a);
+        NoopCC c = new NoopCC(b);
+        a.setPendingCount(1);
+        b.setPendingCount(1);
+        c.setPendingCount(1);
+        c.quietlyCompleteRoot();
+        assertTrue(a.isDone());
+        assertFalse(b.isDone());
+        assertFalse(c.isDone());
+    }
+
+    // Invocation tests use some interdependent task classes
+    // to better test propagation etc
+
+
+    // Version of Fibonacci with different classes for left vs right forks
+    abstract class CCF extends CheckedCC {
+        int number;
+        int rnumber;
+
+        public CCF(CountedCompleter parent, int n) {
+            super(parent, 1);
+            this.number = n;
+        }
+
+        protected final void realCompute() {
+            CCF f = this;
+            int n = number;
+            while (n >= 2) {
+                new RCCF(f, n - 2).fork();
+                f = new LCCF(f, --n);
+            }
+            f.complete(null);
+        }
+    }
+
+    final class LCCF extends CCF {
+        public LCCF(int n) { this(null, n); }
+        public LCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            super.onCompletion(caller);
+            CCF p = (CCF)getCompleter();
+            int n = number + rnumber;
+            if (p != null)
+                p.number = n;
+            else
+                number = n;
+        }
+    }
+    final class RCCF extends CCF {
+        public RCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            super.onCompletion(caller);
+            CCF p = (CCF)getCompleter();
+            int n = number + rnumber;
+            if (p != null)
+                p.rnumber = n;
+            else
+                number = n;
+        }
+    }
+
+    // Version of CCF with forced failure in left completions
+    abstract class FailingCCF extends CheckedCC {
+        int number;
+        int rnumber;
+
+        public FailingCCF(CountedCompleter parent, int n) {
+            super(parent, 1);
+            this.number = n;
+        }
+
+        protected final void realCompute() {
+            FailingCCF f = this;
+            int n = number;
+            while (n >= 2) {
+                new RFCCF(f, n - 2).fork();
+                f = new LFCCF(f, --n);
+            }
+            f.complete(null);
+        }
+    }
+
+    final class LFCCF extends FailingCCF {
+        public LFCCF(int n) { this(null, n); }
+        public LFCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            super.onCompletion(caller);
+            FailingCCF p = (FailingCCF)getCompleter();
+            int n = number + rnumber;
+            if (p != null)
+                p.number = n;
+            else
+                number = n;
+        }
+    }
+    final class RFCCF extends FailingCCF {
+        public RFCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            super.onCompletion(caller);
+            completeExceptionally(new FJException());
+        }
+    }
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks; getRawResult returns null.
+     */
+    public void testInvoke() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvoke() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGet() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGet() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPE() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesce() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                assertEquals(0, getQueuedTaskCount());
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvoke() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvoke() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGet() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGet() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvoke() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGet() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGet() throws Exception {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * getPool of executing task returns its pool
+     */
+    public void testGetPool() {
+        final ForkJoinPool mainPool = mainPool();
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertSame(mainPool, getPool());
+            }};
+        testInvokeOnPool(mainPool, a);
+    }
+
+    /**
+     * getPool of non-FJ task returns null
+     */
+    public void testGetPool2() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertNull(getPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * inForkJoinPool of executing task returns true
+     */
+    public void testInForkJoinPool() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertTrue(inForkJoinPool());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertFalse(inForkJoinPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * setRawResult(null) succeeds
+     */
+    public void testSetRawResult() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                setRawResult(null);
+                assertNull(getRawResult());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally2() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF n = new LCCF(8);
+                CCF f = new LCCF(n, 8);
+                FJException ex = new FJException();
+                f.completeExceptionally(ex);
+                f.checkCompletedExceptionally(ex);
+                n.checkCompletedExceptionally(ex);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                invokeAll(f, g);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.number);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                invokeAll(f, g, h);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollection() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPE() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                FailingCCF g = new LFCCF(9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF g = new LFCCF(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                FailingCCF g = new LFCCF(9);
+                CCF h = new LCCF(7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection)  throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollection() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * tryUnfork returns true for most recent unexecuted task,
+     * and suppresses execution
+     */
+    public void testTryUnfork() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertTrue(f.tryUnfork());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * getSurplusQueuedTaskCount returns > 0 when
+     * there are more tasks than threads
+     */
+    public void testGetSurplusQueuedTaskCount() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF h = new LCCF(7);
+                assertSame(h, h.fork());
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertTrue(getSurplusQueuedTaskCount() > 0);
+                helpQuiesce();
+                assertEquals(0, getSurplusQueuedTaskCount());
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns most recent unexecuted task.
+     */
+    public void testPeekNextLocalTask() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(f, peekNextLocalTask());
+                assertNull(f.join());
+                checkCompletedNormally(f);
+                helpQuiesce();
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns most recent unexecuted task without
+     * executing it
+     */
+    public void testPollNextLocalTask() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollNextLocalTask());
+                helpQuiesce();
+                checkNotDone(f);
+                assertEquals(34, g.number);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it
+     */
+    public void testPollTask() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns least recent unexecuted task in async mode
+     */
+    public void testPeekNextLocalTaskAsync() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(g, peekNextLocalTask());
+                assertNull(f.join());
+                helpQuiesce();
+                checkCompletedNormally(f);
+                assertEquals(34, g.number);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns least recent unexecuted task without
+     * executing it, in async mode
+     */
+    public void testPollNextLocalTaskAsync() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollNextLocalTask());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it, in
+     * async mode
+     */
+    public void testPollTaskAsync() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollTask());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    // versions for singleton pools
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks; getRawResult returns null.
+     */
+    public void testInvokeSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvokeSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGetSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGetSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPESingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesceSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                assertEquals(0, getQueuedTaskCount());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvokeSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvokeSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGetSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGetSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvokeSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGetSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGetSingleton() throws Exception {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionallySingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF n = new LCCF(8);
+                CCF f = new LCCF(n, 8);
+                FJException ex = new FJException();
+                f.completeExceptionally(ex);
+                f.checkCompletedExceptionally(ex);
+                n.checkCompletedExceptionally(ex);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                invokeAll(f, g);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.number);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                invokeAll(f, g, h);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollectionSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPESingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                FailingCCF g = new LFCCF(9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF g = new LFCCF(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                FailingCCF g = new LFCCF(9);
+                CCF h = new LCCF(7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(collection)  throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollectionSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/CyclicBarrierTest.java b/jsr166-tests/src/test/java/jsr166/CyclicBarrierTest.java
new file mode 100644
index 0000000..3239030
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/CyclicBarrierTest.java
@@ -0,0 +1,458 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class CyclicBarrierTest extends JSR166TestCase {
+
+    private volatile int countAction;
+    private class MyAction implements Runnable {
+        public void run() { ++countAction; }
+    }
+
+    /**
+     * Spin-waits till the number of waiters == numberOfWaiters.
+     */
+    void awaitNumberWaiting(CyclicBarrier barrier, int numberOfWaiters) {
+        long startTime = System.nanoTime();
+        while (barrier.getNumberWaiting() != numberOfWaiters) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                fail("timed out");
+            Thread.yield();
+        }
+    }
+
+    /**
+     * Creating with negative parties throws IAE
+     */
+    public void testConstructor1() {
+        try {
+            new CyclicBarrier(-1, (Runnable)null);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Creating with negative parties and no action throws IAE
+     */
+    public void testConstructor2() {
+        try {
+            new CyclicBarrier(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getParties returns the number of parties given in constructor
+     */
+    public void testGetParties() {
+        CyclicBarrier b = new CyclicBarrier(2);
+        assertEquals(2, b.getParties());
+        assertEquals(0, b.getNumberWaiting());
+    }
+
+    /**
+     * A 1-party barrier triggers after single await
+     */
+    public void testSingleParty() throws Exception {
+        CyclicBarrier b = new CyclicBarrier(1);
+        assertEquals(1, b.getParties());
+        assertEquals(0, b.getNumberWaiting());
+        b.await();
+        b.await();
+        assertEquals(0, b.getNumberWaiting());
+    }
+
+    /**
+     * The supplied barrier action is run at barrier
+     */
+    public void testBarrierAction() throws Exception {
+        countAction = 0;
+        CyclicBarrier b = new CyclicBarrier(1, new MyAction());
+        assertEquals(1, b.getParties());
+        assertEquals(0, b.getNumberWaiting());
+        b.await();
+        b.await();
+        assertEquals(0, b.getNumberWaiting());
+        assertEquals(2, countAction);
+    }
+
+    /**
+     * A 2-party/thread barrier triggers after both threads invoke await
+     */
+    public void testTwoParties() throws Exception {
+        final CyclicBarrier b = new CyclicBarrier(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                b.await();
+                b.await();
+                b.await();
+                b.await();
+            }});
+
+        b.await();
+        b.await();
+        b.await();
+        b.await();
+        awaitTermination(t);
+    }
+
+    /**
+     * An interruption in one party causes others waiting in await to
+     * throw BrokenBarrierException
+     */
+    public void testAwait1_Interrupted_BrokenBarrier() {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(2);
+        Thread t1 = new ThreadShouldThrow(InterruptedException.class) {
+            public void realRun() throws Exception {
+                pleaseInterrupt.countDown();
+                c.await();
+            }};
+        Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) {
+            public void realRun() throws Exception {
+                pleaseInterrupt.countDown();
+                c.await();
+            }};
+
+        t1.start();
+        t2.start();
+        await(pleaseInterrupt);
+        t1.interrupt();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * An interruption in one party causes others waiting in timed await to
+     * throw BrokenBarrierException
+     */
+    public void testAwait2_Interrupted_BrokenBarrier() throws Exception {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(2);
+        Thread t1 = new ThreadShouldThrow(InterruptedException.class) {
+            public void realRun() throws Exception {
+                pleaseInterrupt.countDown();
+                c.await(LONG_DELAY_MS, MILLISECONDS);
+            }};
+        Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) {
+            public void realRun() throws Exception {
+                pleaseInterrupt.countDown();
+                c.await(LONG_DELAY_MS, MILLISECONDS);
+            }};
+
+        t1.start();
+        t2.start();
+        await(pleaseInterrupt);
+        t1.interrupt();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A timeout in timed await throws TimeoutException
+     */
+    public void testAwait3_TimeoutException() throws InterruptedException {
+        final CyclicBarrier c = new CyclicBarrier(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                long startTime = System.nanoTime();
+                try {
+                    c.await(timeoutMillis(), MILLISECONDS);
+                    shouldThrow();
+                } catch (TimeoutException success) {}
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * A timeout in one party causes others waiting in timed await to
+     * throw BrokenBarrierException
+     */
+    public void testAwait4_Timeout_BrokenBarrier() throws InterruptedException {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                try {
+                    c.await(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (BrokenBarrierException success) {}
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                awaitNumberWaiting(c, 1);
+                long startTime = System.nanoTime();
+                try {
+                    c.await(timeoutMillis(), MILLISECONDS);
+                    shouldThrow();
+                } catch (TimeoutException success) {}
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A timeout in one party causes others waiting in await to
+     * throw BrokenBarrierException
+     */
+    public void testAwait5_Timeout_BrokenBarrier() throws InterruptedException {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                try {
+                    c.await();
+                    shouldThrow();
+                } catch (BrokenBarrierException success) {}
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                awaitNumberWaiting(c, 1);
+                long startTime = System.nanoTime();
+                try {
+                    c.await(timeoutMillis(), MILLISECONDS);
+                    shouldThrow();
+                } catch (TimeoutException success) {}
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A reset of an active barrier causes waiting threads to throw
+     * BrokenBarrierException
+     */
+    public void testReset_BrokenBarrier() throws InterruptedException {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        final CountDownLatch pleaseReset = new CountDownLatch(2);
+        Thread t1 = new ThreadShouldThrow(BrokenBarrierException.class) {
+            public void realRun() throws Exception {
+                pleaseReset.countDown();
+                c.await();
+            }};
+        Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) {
+            public void realRun() throws Exception {
+                pleaseReset.countDown();
+                c.await();
+            }};
+
+        t1.start();
+        t2.start();
+        await(pleaseReset);
+
+        awaitNumberWaiting(c, 2);
+        c.reset();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A reset before threads enter barrier does not throw
+     * BrokenBarrierException
+     */
+    public void testReset_NoBrokenBarrier() throws Exception {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        c.reset();
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                c.await();
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                c.await();
+            }});
+
+        c.await();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * All threads block while a barrier is broken.
+     */
+    public void testReset_Leakage() throws InterruptedException {
+        final CyclicBarrier c = new CyclicBarrier(2);
+        final AtomicBoolean done = new AtomicBoolean();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                while (!done.get()) {
+                    try {
+                        while (c.isBroken())
+                            c.reset();
+
+                        c.await();
+                        shouldThrow();
+                    }
+                    catch (BrokenBarrierException ok) {}
+                    catch (InterruptedException ok) {}
+                }}});
+
+        for (int i = 0; i < 4; i++) {
+            delay(timeoutMillis());
+            t.interrupt();
+        }
+        done.set(true);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * Reset of a non-broken barrier does not break barrier
+     */
+    public void testResetWithoutBreakage() throws Exception {
+        final CyclicBarrier barrier = new CyclicBarrier(3);
+        for (int i = 0; i < 3; i++) {
+            final CyclicBarrier start = new CyclicBarrier(3);
+            Thread t1 = newStartedThread(new CheckedRunnable() {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }});
+
+            Thread t2 = newStartedThread(new CheckedRunnable() {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }});
+
+            start.await();
+            barrier.await();
+            awaitTermination(t1);
+            awaitTermination(t2);
+            assertFalse(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+            if (i == 1) barrier.reset();
+            assertFalse(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+        }
+    }
+
+    /**
+     * Reset of a barrier after interruption reinitializes it.
+     */
+    public void testResetAfterInterrupt() throws Exception {
+        final CyclicBarrier barrier = new CyclicBarrier(3);
+        for (int i = 0; i < 2; i++) {
+            final CyclicBarrier start = new CyclicBarrier(3);
+            Thread t1 = new ThreadShouldThrow(InterruptedException.class) {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }};
+
+            Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }};
+
+            t1.start();
+            t2.start();
+            start.await();
+            t1.interrupt();
+            awaitTermination(t1);
+            awaitTermination(t2);
+            assertTrue(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+            barrier.reset();
+            assertFalse(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+        }
+    }
+
+    /**
+     * Reset of a barrier after timeout reinitializes it.
+     */
+    public void testResetAfterTimeout() throws Exception {
+        final CyclicBarrier barrier = new CyclicBarrier(3);
+        for (int i = 0; i < 2; i++) {
+            assertEquals(0, barrier.getNumberWaiting());
+            Thread t1 = newStartedThread(new CheckedRunnable() {
+                public void realRun() throws Exception {
+                    try {
+                        barrier.await();
+                        shouldThrow();
+                    } catch (BrokenBarrierException success) {}
+                }});
+            Thread t2 = newStartedThread(new CheckedRunnable() {
+                public void realRun() throws Exception {
+                    awaitNumberWaiting(barrier, 1);
+                    long startTime = System.nanoTime();
+                    try {
+                        barrier.await(timeoutMillis(), MILLISECONDS);
+                        shouldThrow();
+                    } catch (TimeoutException success) {}
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }});
+
+            awaitTermination(t1);
+            awaitTermination(t2);
+            assertEquals(0, barrier.getNumberWaiting());
+            assertTrue(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+            barrier.reset();
+            assertFalse(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+        }
+    }
+
+    /**
+     * Reset of a barrier after a failed command reinitializes it.
+     */
+    public void testResetAfterCommandException() throws Exception {
+        final CyclicBarrier barrier =
+            new CyclicBarrier(3, new Runnable() {
+                    public void run() {
+                        throw new NullPointerException(); }});
+        for (int i = 0; i < 2; i++) {
+            final CyclicBarrier start = new CyclicBarrier(3);
+            Thread t1 = new ThreadShouldThrow(BrokenBarrierException.class) {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }};
+
+            Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }};
+
+            t1.start();
+            t2.start();
+            start.await();
+            awaitNumberWaiting(barrier, 2);
+            try {
+                barrier.await();
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            awaitTermination(t1);
+            awaitTermination(t2);
+            assertTrue(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+            barrier.reset();
+            assertFalse(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+        }
+    }
+}
diff --git a/jsr166-tests/src/test/java/jsr166/DelayQueueTest.java b/jsr166-tests/src/test/java/jsr166/DelayQueueTest.java
new file mode 100644
index 0000000..5d48c18
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/DelayQueueTest.java
@@ -0,0 +1,767 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.DelayQueue;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class DelayQueueTest extends JSR166TestCase {
+
+    public static class Generic extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new DelayQueue();
+        }
+        protected PDelay makeElement(int i) {
+            return new PDelay(i);
+        }
+    }
+
+    private static final int NOCAP = Integer.MAX_VALUE;
+
+    /**
+     * A delayed implementation for testing.
+     * Most tests use Pseudodelays, where delays are all elapsed
+     * (so, no blocking solely for delays) but are still ordered
+     */
+    static class PDelay implements Delayed {
+        int pseudodelay;
+        PDelay(int i) { pseudodelay = i; }
+        public int compareTo(PDelay other) {
+            int a = this.pseudodelay;
+            int b = other.pseudodelay;
+            return (a < b) ? -1 : (a > b) ? 1 : 0;
+        }
+        public int compareTo(Delayed y) {
+            return compareTo((PDelay)y);
+        }
+        public boolean equals(Object other) {
+            return (other instanceof PDelay) &&
+                this.pseudodelay == ((PDelay)other).pseudodelay;
+        }
+        // suppress [overrides] javac warning
+        public int hashCode() { return pseudodelay; }
+        public long getDelay(TimeUnit ignore) {
+            return Integer.MIN_VALUE + pseudodelay;
+        }
+        public String toString() {
+            return String.valueOf(pseudodelay);
+        }
+    }
+
+    /**
+     * Delayed implementation that actually delays
+     */
+    static class NanoDelay implements Delayed {
+        long trigger;
+        NanoDelay(long i) {
+            trigger = System.nanoTime() + i;
+        }
+        public int compareTo(NanoDelay y) {
+            long i = trigger;
+            long j = y.trigger;
+            if (i < j) return -1;
+            if (i > j) return 1;
+            return 0;
+        }
+
+        public int compareTo(Delayed y) {
+            return compareTo((NanoDelay)y);
+        }
+
+        public boolean equals(Object other) {
+            return equals((NanoDelay)other);
+        }
+        public boolean equals(NanoDelay other) {
+            return other.trigger == trigger;
+        }
+
+        // suppress [overrides] javac warning
+        public int hashCode() { return (int) trigger; }
+
+        public long getDelay(TimeUnit unit) {
+            long n = trigger - System.nanoTime();
+            return unit.convert(n, TimeUnit.NANOSECONDS);
+        }
+
+        public long getTriggerTime() {
+            return trigger;
+        }
+
+        public String toString() {
+            return String.valueOf(trigger);
+        }
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * PDelays 0 ... n.
+     */
+    private DelayQueue<PDelay> populatedQueue(int n) {
+        DelayQueue<PDelay> q = new DelayQueue<PDelay>();
+        assertTrue(q.isEmpty());
+        for (int i = n-1; i >= 0; i-=2)
+            assertTrue(q.offer(new PDelay(i)));
+        for (int i = (n & 1); i < n; i+=2)
+            assertTrue(q.offer(new PDelay(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(NOCAP, q.remainingCapacity());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * A new queue has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(NOCAP, new DelayQueue().remainingCapacity());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            DelayQueue q = new DelayQueue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            PDelay[] ints = new PDelay[SIZE];
+            DelayQueue q = new DelayQueue(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        try {
+            PDelay[] ints = new PDelay[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new PDelay(i);
+            DelayQueue q = new DelayQueue(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        PDelay[] ints = new PDelay[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new PDelay(i);
+        DelayQueue q = new DelayQueue(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        DelayQueue q = new DelayQueue();
+        assertTrue(q.isEmpty());
+        assertEquals(NOCAP, q.remainingCapacity());
+        q.add(new PDelay(1));
+        assertFalse(q.isEmpty());
+        q.add(new PDelay(2));
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * remainingCapacity does not change when elements added or removed,
+     * but size does
+     */
+    public void testRemainingCapacity() {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(NOCAP, q.remainingCapacity());
+            assertEquals(SIZE-i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(NOCAP, q.remainingCapacity());
+            assertEquals(i, q.size());
+            q.add(new PDelay(i));
+        }
+    }
+
+    /**
+     * offer non-null succeeds
+     */
+    public void testOffer() {
+        DelayQueue q = new DelayQueue();
+        assertTrue(q.offer(new PDelay(0)));
+        assertTrue(q.offer(new PDelay(1)));
+    }
+
+    /**
+     * add succeeds
+     */
+    public void testAdd() {
+        DelayQueue q = new DelayQueue();
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            assertTrue(q.add(new PDelay(i)));
+        }
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        try {
+            DelayQueue q = populatedQueue(SIZE);
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        try {
+            DelayQueue q = new DelayQueue();
+            PDelay[] ints = new PDelay[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new PDelay(i);
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        PDelay[] empty = new PDelay[0];
+        PDelay[] ints = new PDelay[SIZE];
+        for (int i = SIZE-1; i >= 0; --i)
+            ints[i] = new PDelay(i);
+        DelayQueue q = new DelayQueue();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() {
+        DelayQueue q = new DelayQueue();
+        for (int i = 0; i < SIZE; ++i) {
+            PDelay I = new PDelay(i);
+            q.put(I);
+            assertTrue(q.contains(I));
+        }
+        assertEquals(SIZE, q.size());
+    }
+
+    /**
+     * put doesn't block waiting for take
+     */
+    public void testPutWithTake() throws InterruptedException {
+        final DelayQueue q = new DelayQueue();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                q.put(new PDelay(0));
+                q.put(new PDelay(0));
+                q.put(new PDelay(0));
+                q.put(new PDelay(0));
+            }});
+
+        awaitTermination(t);
+        assertEquals(4, q.size());
+    }
+
+    /**
+     * timed offer does not time out
+     */
+    public void testTimedOffer() throws InterruptedException {
+        final DelayQueue q = new DelayQueue();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new PDelay(0));
+                q.put(new PDelay(0));
+                assertTrue(q.offer(new PDelay(0), SHORT_DELAY_MS, MILLISECONDS));
+                assertTrue(q.offer(new PDelay(0), LONG_DELAY_MS, MILLISECONDS));
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in priority order
+     */
+    public void testTake() throws InterruptedException {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), ((PDelay)q.take()));
+        }
+    }
+
+    /**
+     * Take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final DelayQueue q = populatedQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(new PDelay(i), ((PDelay)q.take()));
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), ((PDelay)q.poll()));
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), ((PDelay)q.poll(0, MILLISECONDS)));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(new PDelay(i), ((PDelay)q.poll(LONG_DELAY_MS, MILLISECONDS)));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                DelayQueue q = populatedQueue(SIZE);
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(new PDelay(i), ((PDelay)q.poll(SHORT_DELAY_MS, MILLISECONDS)));
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), ((PDelay)q.peek()));
+            assertEquals(new PDelay(i), ((PDelay)q.poll()));
+            if (q.isEmpty())
+                assertNull(q.peek());
+            else
+                assertFalse(new PDelay(i).equals(q.peek()));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), ((PDelay)q.element()));
+            q.poll();
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), ((PDelay)q.remove()));
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new PDelay(i)));
+            q.poll();
+            assertFalse(q.contains(new PDelay(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        DelayQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertEquals(NOCAP, q.remainingCapacity());
+        PDelay x = new PDelay(1);
+        q.add(x);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(x));
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        DelayQueue q = populatedQueue(SIZE);
+        DelayQueue p = new DelayQueue();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new PDelay(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        DelayQueue q = populatedQueue(SIZE);
+        DelayQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE-i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            DelayQueue q = populatedQueue(SIZE);
+            DelayQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE-i, q.size());
+            for (int j = 0; j < i; ++j) {
+                PDelay I = (PDelay)(p.remove());
+                assertFalse(q.contains(I));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements
+     */
+    public void testToArray() throws InterruptedException {
+        DelayQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        Arrays.sort(o);
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.take());
+    }
+
+    /**
+     * toArray(a) contains all elements
+     */
+    public void testToArray2() {
+        DelayQueue<PDelay> q = populatedQueue(SIZE);
+        PDelay[] ints = new PDelay[SIZE];
+        PDelay[] array = q.toArray(ints);
+        assertSame(ints, array);
+        Arrays.sort(ints);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.remove());
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        DelayQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        DelayQueue q = populatedQueue(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final DelayQueue q = new DelayQueue();
+        q.add(new PDelay(2));
+        q.add(new PDelay(1));
+        q.add(new PDelay(3));
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+        it = q.iterator();
+        assertEquals(new PDelay(2), it.next());
+        assertEquals(new PDelay(3), it.next());
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        DelayQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (Object e : q)
+            assertTrue(s.contains(e.toString()));
+    }
+
+    /**
+     * timed poll transfers elements across Executor tasks
+     */
+    public void testPollInExecutor() {
+        final DelayQueue q = new DelayQueue();
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        ExecutorService executor = Executors.newFixedThreadPool(2);
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertNull(q.poll());
+                threadsStarted.await();
+                assertNotNull(q.poll(LONG_DELAY_MS, MILLISECONDS));
+                checkEmpty(q);
+            }});
+
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadsStarted.await();
+                q.put(new PDelay(1));
+            }});
+
+        joinPool(executor);
+    }
+
+    /**
+     * Delayed actions do not occur until their delay elapses
+     */
+    public void testDelay() throws InterruptedException {
+        DelayQueue<NanoDelay> q = new DelayQueue<NanoDelay>();
+        for (int i = 0; i < SIZE; ++i)
+            q.add(new NanoDelay(1000000L * (SIZE - i)));
+
+        long last = 0;
+        for (int i = 0; i < SIZE; ++i) {
+            NanoDelay e = q.take();
+            long tt = e.getTriggerTime();
+            assertTrue(System.nanoTime() - tt >= 0);
+            if (i != 0)
+                assertTrue(tt >= last);
+            last = tt;
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * peek of a non-empty queue returns non-null even if not expired
+     */
+    public void testPeekDelayed() {
+        DelayQueue q = new DelayQueue();
+        q.add(new NanoDelay(Long.MAX_VALUE));
+        assertNotNull(q.peek());
+    }
+
+    /**
+     * poll of a non-empty queue returns null if no expired elements.
+     */
+    public void testPollDelayed() {
+        DelayQueue q = new DelayQueue();
+        q.add(new NanoDelay(Long.MAX_VALUE));
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll of a non-empty queue returns null if no expired elements.
+     */
+    public void testTimedPollDelayed() throws InterruptedException {
+        DelayQueue q = new DelayQueue();
+        q.add(new NanoDelay(LONG_DELAY_MS * 1000000L));
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+    }
+
+    /**
+     * drainTo(c) empties queue into another collection c
+     */
+    public void testDrainTo() {
+        DelayQueue q = new DelayQueue();
+        PDelay[] elems = new PDelay[SIZE];
+        for (int i = 0; i < SIZE; ++i) {
+            elems[i] = new PDelay(i);
+            q.add(elems[i]);
+        }
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(elems[i], l.get(i));
+        q.add(elems[0]);
+        q.add(elems[1]);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(elems[0]));
+        assertTrue(q.contains(elems[1]));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i)
+            assertEquals(elems[i], l.get(i));
+    }
+
+    /**
+     * drainTo empties queue
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final DelayQueue q = populatedQueue(SIZE);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                q.put(new PDelay(SIZE+1));
+            }});
+
+        t.start();
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        t.join();
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        for (int i = 0; i < SIZE + 2; ++i) {
+            DelayQueue q = populatedQueue(SIZE);
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(SIZE-k, q.size());
+            assertEquals(k, l.size());
+        }
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/EntryTest.java b/jsr166-tests/src/test/java/jsr166/EntryTest.java
new file mode 100644
index 0000000..4387a53
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/EntryTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+
+public class EntryTest extends JSR166TestCase {
+
+    static final String k1 = "1";
+    static final String v1 = "a";
+    static final String k2 = "2";
+    static final String v2 = "b";
+
+    /**
+     * A new SimpleEntry(k, v) holds k, v.
+     */
+    public void testConstructor1() {
+        Map.Entry e = new AbstractMap.SimpleEntry(k1, v1);
+        assertEquals(k1, e.getKey());
+        assertEquals(v1, e.getValue());
+    }
+
+    /**
+     * A new SimpleImmutableEntry(k, v) holds k, v.
+     */
+    public void testConstructor2() {
+        Map.Entry s = new AbstractMap.SimpleImmutableEntry(k1, v1);
+        assertEquals(k1, s.getKey());
+        assertEquals(v1, s.getValue());
+    }
+
+    /**
+     * A new SimpleEntry(entry(k, v)) holds k, v.
+     */
+    public void testConstructor3() {
+        Map.Entry e2 = new AbstractMap.SimpleEntry(k1, v1);
+        Map.Entry e = new AbstractMap.SimpleEntry(e2);
+        assertEquals(k1, e.getKey());
+        assertEquals(v1, e.getValue());
+    }
+
+    /**
+     * A new SimpleImmutableEntry(entry(k, v)) holds k, v.
+     */
+    public void testConstructor4() {
+        Map.Entry s2 = new AbstractMap.SimpleImmutableEntry(k1, v1);
+        Map.Entry s = new AbstractMap.SimpleImmutableEntry(s2);
+        assertEquals(k1, s.getKey());
+        assertEquals(v1, s.getValue());
+    }
+
+    /**
+     * Entries with same key-value pairs are equal and have same
+     * hashcodes
+     */
+    public void testEquals() {
+        Map.Entry e2 = new AbstractMap.SimpleEntry(k1, v1);
+        Map.Entry e = new AbstractMap.SimpleEntry(e2);
+        Map.Entry s2 = new AbstractMap.SimpleImmutableEntry(k1, v1);
+        Map.Entry s = new AbstractMap.SimpleImmutableEntry(s2);
+        assertEquals(e2, e);
+        assertEquals(e2.hashCode(), e.hashCode());
+        assertEquals(s2, s);
+        assertEquals(s2.hashCode(), s.hashCode());
+        assertEquals(e2, s2);
+        assertEquals(e2.hashCode(), s2.hashCode());
+        assertEquals(e, s);
+        assertEquals(e.hashCode(), s.hashCode());
+    }
+
+    /**
+     * Entries with different key-value pairs are not equal
+     */
+    public void testNotEquals() {
+        Map.Entry e2 = new AbstractMap.SimpleEntry(k1, v1);
+        Map.Entry e = new AbstractMap.SimpleEntry(k2, v1);
+        assertFalse(e2.equals(e));
+        e = new AbstractMap.SimpleEntry(k1, v2);
+        assertFalse(e2.equals(e));
+        e = new AbstractMap.SimpleEntry(k2, v2);
+        assertFalse(e2.equals(e));
+
+        Map.Entry s2 = new AbstractMap.SimpleImmutableEntry(k1, v1);
+        Map.Entry s = new AbstractMap.SimpleImmutableEntry(k2, v1);
+        assertFalse(s2.equals(s));
+        s = new AbstractMap.SimpleImmutableEntry(k1, v2);
+        assertFalse(s2.equals(s));
+        s = new AbstractMap.SimpleImmutableEntry(k2, v2);
+        assertFalse(s2.equals(s));
+    }
+
+    /**
+     * getValue returns last setValue for SimpleEntry
+     */
+    public void testSetValue1() {
+        Map.Entry e2 = new AbstractMap.SimpleEntry(k1, v1);
+        Map.Entry e = new AbstractMap.SimpleEntry(e2);
+        assertEquals(k1, e.getKey());
+        assertEquals(v1, e.getValue());
+        e.setValue(k2);
+        assertEquals(k2, e.getValue());
+        assertFalse(e2.equals(e));
+    }
+
+    /**
+     * setValue for SimpleImmutableEntry throws UnsupportedOperationException
+     */
+    public void testSetValue2() {
+        Map.Entry s2 = new AbstractMap.SimpleImmutableEntry(k1, v1);
+        Map.Entry s = new AbstractMap.SimpleImmutableEntry(s2);
+        assertEquals(k1, s.getKey());
+        assertEquals(v1, s.getValue());
+        try {
+            s.setValue(k2);
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ExchangerTest.java b/jsr166-tests/src/test/java/jsr166/ExchangerTest.java
new file mode 100644
index 0000000..b0f325e
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ExchangerTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Exchanger;
+import java.util.concurrent.TimeoutException;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class ExchangerTest extends JSR166TestCase {
+
+    /**
+     * exchange exchanges objects across two threads
+     */
+    public void testExchange() {
+        final Exchanger e = new Exchanger();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertSame(one, e.exchange(two));
+                assertSame(two, e.exchange(one));
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertSame(two, e.exchange(one));
+                assertSame(one, e.exchange(two));
+            }});
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * timed exchange exchanges objects across two threads
+     */
+    public void testTimedExchange() {
+        final Exchanger e = new Exchanger();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                assertSame(one, e.exchange(two, LONG_DELAY_MS, MILLISECONDS));
+                assertSame(two, e.exchange(one, LONG_DELAY_MS, MILLISECONDS));
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                assertSame(two, e.exchange(one, LONG_DELAY_MS, MILLISECONDS));
+                assertSame(one, e.exchange(two, LONG_DELAY_MS, MILLISECONDS));
+            }});
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * interrupt during wait for exchange throws IE
+     */
+    public void testExchange_InterruptedException() {
+        final Exchanger e = new Exchanger();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadStarted.countDown();
+                e.exchange(one);
+            }});
+
+        await(threadStarted);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * interrupt during wait for timed exchange throws IE
+     */
+    public void testTimedExchange_InterruptedException() {
+        final Exchanger e = new Exchanger();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws Exception {
+                threadStarted.countDown();
+                e.exchange(null, LONG_DELAY_MS, MILLISECONDS);
+            }});
+
+        await(threadStarted);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timeout during wait for timed exchange throws TimeoutException
+     */
+    public void testExchange_TimeoutException() {
+        final Exchanger e = new Exchanger();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                long startTime = System.nanoTime();
+                try {
+                    e.exchange(null, timeoutMillis(), MILLISECONDS);
+                    shouldThrow();
+                } catch (TimeoutException success) {}
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * If one exchanging thread is interrupted, another succeeds.
+     */
+    public void testReplacementAfterExchange() {
+        final Exchanger e = new Exchanger();
+        final CountDownLatch exchanged = new CountDownLatch(2);
+        final CountDownLatch interrupted = new CountDownLatch(1);
+        Thread t1 = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertSame(two, e.exchange(one));
+                exchanged.countDown();
+                e.exchange(two);
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertSame(one, e.exchange(two));
+                exchanged.countDown();
+                interrupted.await();
+                assertSame(three, e.exchange(one));
+            }});
+        Thread t3 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                interrupted.await();
+                assertSame(one, e.exchange(three));
+            }});
+
+        await(exchanged);
+        t1.interrupt();
+        awaitTermination(t1);
+        interrupted.countDown();
+        awaitTermination(t2);
+        awaitTermination(t3);
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ExecutorCompletionServiceTest.java b/jsr166-tests/src/test/java/jsr166/ExecutorCompletionServiceTest.java
new file mode 100644
index 0000000..eced0ba
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ExecutorCompletionServiceTest.java
@@ -0,0 +1,221 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.security.*;
+
+public class ExecutorCompletionServiceTest extends JSR166TestCase {
+
+    /**
+     * Creating a new ECS with null Executor throw NPE
+     */
+    public void testConstructorNPE() {
+        try {
+            ExecutorCompletionService ecs = new ExecutorCompletionService(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Creating a new ECS with null queue throw NPE
+     */
+    public void testConstructorNPE2() {
+        try {
+            ExecutorService e = Executors.newCachedThreadPool();
+            ExecutorCompletionService ecs = new ExecutorCompletionService(e, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Submitting a null callable throws NPE
+     */
+    public void testSubmitNPE() {
+        ExecutorService e = Executors.newCachedThreadPool();
+        ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+        try {
+            Callable c = null;
+            ecs.submit(c);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * Submitting a null runnable throws NPE
+     */
+    public void testSubmitNPE2() {
+        ExecutorService e = Executors.newCachedThreadPool();
+        ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+        try {
+            Runnable r = null;
+            ecs.submit(r, Boolean.TRUE);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * A taken submitted task is completed
+     */
+    public void testTake() throws InterruptedException {
+        ExecutorService e = Executors.newCachedThreadPool();
+        ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+        try {
+            Callable c = new StringTask();
+            ecs.submit(c);
+            Future f = ecs.take();
+            assertTrue(f.isDone());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * Take returns the same future object returned by submit
+     */
+    public void testTake2() throws InterruptedException {
+        ExecutorService e = Executors.newCachedThreadPool();
+        ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+        try {
+            Callable c = new StringTask();
+            Future f1 = ecs.submit(c);
+            Future f2 = ecs.take();
+            assertSame(f1, f2);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * If poll returns non-null, the returned task is completed
+     */
+    public void testPoll1() throws Exception {
+        ExecutorService e = Executors.newCachedThreadPool();
+        ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+        try {
+            assertNull(ecs.poll());
+            Callable c = new StringTask();
+            ecs.submit(c);
+
+            long startTime = System.nanoTime();
+            Future f;
+            while ((f = ecs.poll()) == null) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    fail("timed out");
+                Thread.yield();
+            }
+            assertTrue(f.isDone());
+            assertSame(TEST_STRING, f.get());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * If timed poll returns non-null, the returned task is completed
+     */
+    public void testPoll2() throws InterruptedException {
+        ExecutorService e = Executors.newCachedThreadPool();
+        ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+        try {
+            assertNull(ecs.poll());
+            Callable c = new StringTask();
+            ecs.submit(c);
+            Future f = ecs.poll(SHORT_DELAY_MS, MILLISECONDS);
+            if (f != null)
+                assertTrue(f.isDone());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * Submitting to underlying AES that overrides newTaskFor(Callable)
+     * returns and eventually runs Future returned by newTaskFor.
+     */
+    public void testNewTaskForCallable() throws InterruptedException {
+        final AtomicBoolean done = new AtomicBoolean(false);
+        class MyCallableFuture<V> extends FutureTask<V> {
+            MyCallableFuture(Callable<V> c) { super(c); }
+            protected void done() { done.set(true); }
+        }
+        ExecutorService e = new ThreadPoolExecutor(
+                                 1, 1, 30L, TimeUnit.SECONDS,
+                                 new ArrayBlockingQueue<Runnable>(1)) {
+            protected <T> RunnableFuture<T> newTaskFor(Callable<T> c) {
+                return new MyCallableFuture<T>(c);
+            }};
+        ExecutorCompletionService<String> ecs =
+            new ExecutorCompletionService<String>(e);
+        try {
+            assertNull(ecs.poll());
+            Callable<String> c = new StringTask();
+            Future f1 = ecs.submit(c);
+            assertTrue("submit must return MyCallableFuture",
+                       f1 instanceof MyCallableFuture);
+            Future f2 = ecs.take();
+            assertSame("submit and take must return same objects", f1, f2);
+            assertTrue("completed task must have set done", done.get());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * Submitting to underlying AES that overrides newTaskFor(Runnable,T)
+     * returns and eventually runs Future returned by newTaskFor.
+     */
+    public void testNewTaskForRunnable() throws InterruptedException {
+        final AtomicBoolean done = new AtomicBoolean(false);
+        class MyRunnableFuture<V> extends FutureTask<V> {
+            MyRunnableFuture(Runnable t, V r) { super(t, r); }
+            protected void done() { done.set(true); }
+        }
+        ExecutorService e = new ThreadPoolExecutor(
+                                 1, 1, 30L, TimeUnit.SECONDS,
+                                 new ArrayBlockingQueue<Runnable>(1)) {
+            protected <T> RunnableFuture<T> newTaskFor(Runnable t, T r) {
+                return new MyRunnableFuture<T>(t, r);
+            }};
+        ExecutorCompletionService<String> ecs =
+            new ExecutorCompletionService<String>(e);
+        try {
+            assertNull(ecs.poll());
+            Runnable r = new NoOpRunnable();
+            Future f1 = ecs.submit(r, null);
+            assertTrue("submit must return MyRunnableFuture",
+                       f1 instanceof MyRunnableFuture);
+            Future f2 = ecs.take();
+            assertSame("submit and take must return same objects", f1, f2);
+            assertTrue("completed task must have set done", done.get());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ExecutorsTest.java b/jsr166-tests/src/test/java/jsr166/ExecutorsTest.java
new file mode 100644
index 0000000..18c0975
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ExecutorsTest.java
@@ -0,0 +1,585 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+import java.util.concurrent.*;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import java.security.*;
+
+public class ExecutorsTest extends JSR166TestCase {
+
+    /**
+     * A newCachedThreadPool can execute runnables
+     */
+    public void testNewCachedThreadPool1() {
+        ExecutorService e = Executors.newCachedThreadPool();
+        e.execute(new NoOpRunnable());
+        e.execute(new NoOpRunnable());
+        e.execute(new NoOpRunnable());
+        joinPool(e);
+    }
+
+    /**
+     * A newCachedThreadPool with given ThreadFactory can execute runnables
+     */
+    public void testNewCachedThreadPool2() {
+        ExecutorService e = Executors.newCachedThreadPool(new SimpleThreadFactory());
+        e.execute(new NoOpRunnable());
+        e.execute(new NoOpRunnable());
+        e.execute(new NoOpRunnable());
+        joinPool(e);
+    }
+
+    /**
+     * A newCachedThreadPool with null ThreadFactory throws NPE
+     */
+    public void testNewCachedThreadPool3() {
+        try {
+            ExecutorService e = Executors.newCachedThreadPool(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A new SingleThreadExecutor can execute runnables
+     */
+    public void testNewSingleThreadExecutor1() {
+        ExecutorService e = Executors.newSingleThreadExecutor();
+        e.execute(new NoOpRunnable());
+        e.execute(new NoOpRunnable());
+        e.execute(new NoOpRunnable());
+        joinPool(e);
+    }
+
+    /**
+     * A new SingleThreadExecutor with given ThreadFactory can execute runnables
+     */
+    public void testNewSingleThreadExecutor2() {
+        ExecutorService e = Executors.newSingleThreadExecutor(new SimpleThreadFactory());
+        e.execute(new NoOpRunnable());
+        e.execute(new NoOpRunnable());
+        e.execute(new NoOpRunnable());
+        joinPool(e);
+    }
+
+    /**
+     * A new SingleThreadExecutor with null ThreadFactory throws NPE
+     */
+    public void testNewSingleThreadExecutor3() {
+        try {
+            ExecutorService e = Executors.newSingleThreadExecutor(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A new SingleThreadExecutor cannot be casted to concrete implementation
+     */
+    public void testCastNewSingleThreadExecutor() {
+        ExecutorService e = Executors.newSingleThreadExecutor();
+        try {
+            ThreadPoolExecutor tpe = (ThreadPoolExecutor)e;
+            shouldThrow();
+        } catch (ClassCastException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * A new newFixedThreadPool can execute runnables
+     */
+    public void testNewFixedThreadPool1() {
+        ExecutorService e = Executors.newFixedThreadPool(2);
+        e.execute(new NoOpRunnable());
+        e.execute(new NoOpRunnable());
+        e.execute(new NoOpRunnable());
+        joinPool(e);
+    }
+
+    /**
+     * A new newFixedThreadPool with given ThreadFactory can execute runnables
+     */
+    public void testNewFixedThreadPool2() {
+        ExecutorService e = Executors.newFixedThreadPool(2, new SimpleThreadFactory());
+        e.execute(new NoOpRunnable());
+        e.execute(new NoOpRunnable());
+        e.execute(new NoOpRunnable());
+        joinPool(e);
+    }
+
+    /**
+     * A new newFixedThreadPool with null ThreadFactory throws NPE
+     */
+    public void testNewFixedThreadPool3() {
+        try {
+            ExecutorService e = Executors.newFixedThreadPool(2, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A new newFixedThreadPool with 0 threads throws IAE
+     */
+    public void testNewFixedThreadPool4() {
+        try {
+            ExecutorService e = Executors.newFixedThreadPool(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * An unconfigurable newFixedThreadPool can execute runnables
+     */
+    public void testUnconfigurableExecutorService() {
+        ExecutorService e = Executors.unconfigurableExecutorService(Executors.newFixedThreadPool(2));
+        e.execute(new NoOpRunnable());
+        e.execute(new NoOpRunnable());
+        e.execute(new NoOpRunnable());
+        joinPool(e);
+    }
+
+    /**
+     * unconfigurableExecutorService(null) throws NPE
+     */
+    public void testUnconfigurableExecutorServiceNPE() {
+        try {
+            ExecutorService e = Executors.unconfigurableExecutorService(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * unconfigurableScheduledExecutorService(null) throws NPE
+     */
+    public void testUnconfigurableScheduledExecutorServiceNPE() {
+        try {
+            ExecutorService e = Executors.unconfigurableScheduledExecutorService(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * a newSingleThreadScheduledExecutor successfully runs delayed task
+     */
+    public void testNewSingleThreadScheduledExecutor() throws Exception {
+        ScheduledExecutorService p = Executors.newSingleThreadScheduledExecutor();
+        try {
+            final CountDownLatch proceed = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    await(proceed);
+                }};
+            long startTime = System.nanoTime();
+            Future f = p.schedule(Executors.callable(task, Boolean.TRUE),
+                                  timeoutMillis(), MILLISECONDS);
+            assertFalse(f.isDone());
+            proceed.countDown();
+            assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS));
+            assertSame(Boolean.TRUE, f.get());
+            assertTrue(f.isDone());
+            assertFalse(f.isCancelled());
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * a newScheduledThreadPool successfully runs delayed task
+     */
+    public void testNewScheduledThreadPool() throws Exception {
+        ScheduledExecutorService p = Executors.newScheduledThreadPool(2);
+        try {
+            final CountDownLatch proceed = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    await(proceed);
+                }};
+            long startTime = System.nanoTime();
+            Future f = p.schedule(Executors.callable(task, Boolean.TRUE),
+                                  timeoutMillis(), MILLISECONDS);
+            assertFalse(f.isDone());
+            proceed.countDown();
+            assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS));
+            assertSame(Boolean.TRUE, f.get());
+            assertTrue(f.isDone());
+            assertFalse(f.isCancelled());
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * an unconfigurable newScheduledThreadPool successfully runs delayed task
+     */
+    public void testUnconfigurableScheduledExecutorService() throws Exception {
+        ScheduledExecutorService p =
+            Executors.unconfigurableScheduledExecutorService
+            (Executors.newScheduledThreadPool(2));
+        try {
+            final CountDownLatch proceed = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    await(proceed);
+                }};
+            long startTime = System.nanoTime();
+            Future f = p.schedule(Executors.callable(task, Boolean.TRUE),
+                                  timeoutMillis(), MILLISECONDS);
+            assertFalse(f.isDone());
+            proceed.countDown();
+            assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS));
+            assertSame(Boolean.TRUE, f.get());
+            assertTrue(f.isDone());
+            assertFalse(f.isCancelled());
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * Future.get on submitted tasks will time out if they compute too long.
+     */
+    public void testTimedCallable() throws Exception {
+        final ExecutorService[] executors = {
+            Executors.newSingleThreadExecutor(),
+            Executors.newCachedThreadPool(),
+            Executors.newFixedThreadPool(2),
+            Executors.newScheduledThreadPool(2),
+        };
+
+        final Runnable sleeper = new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                delay(LONG_DELAY_MS);
+            }};
+
+        List<Thread> threads = new ArrayList<Thread>();
+        for (final ExecutorService executor : executors) {
+            threads.add(newStartedThread(new CheckedRunnable() {
+                public void realRun() {
+                    long startTime = System.nanoTime();
+                    Future future = executor.submit(sleeper);
+                    assertFutureTimesOut(future);
+                }}));
+        }
+        for (Thread thread : threads)
+            awaitTermination(thread);
+        for (ExecutorService executor : executors)
+            joinPool(executor);
+    }
+
+    /**
+     * ThreadPoolExecutor using defaultThreadFactory has
+     * specified group, priority, daemon status, and name
+     */
+    public void testDefaultThreadFactory() throws Exception {
+        final ThreadGroup egroup = Thread.currentThread().getThreadGroup();
+        final CountDownLatch done = new CountDownLatch(1);
+        Runnable r = new CheckedRunnable() {
+            public void realRun() {
+                try {
+                    Thread current = Thread.currentThread();
+                    assertTrue(!current.isDaemon());
+                    assertTrue(current.getPriority() <= Thread.NORM_PRIORITY);
+                    ThreadGroup g = current.getThreadGroup();
+                    SecurityManager s = System.getSecurityManager();
+                    if (s != null)
+                        assertTrue(g == s.getThreadGroup());
+                    else
+                        assertTrue(g == egroup);
+                    String name = current.getName();
+                    assertTrue(name.endsWith("thread-1"));
+                } catch (SecurityException ok) {
+                    // Also pass if not allowed to change setting
+                }
+                done.countDown();
+            }};
+        ExecutorService e = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory());
+
+        e.execute(r);
+        await(done);
+
+        try {
+            e.shutdown();
+        } catch (SecurityException ok) {
+        }
+
+        joinPool(e);
+    }
+
+    /**
+     * ThreadPoolExecutor using privilegedThreadFactory has
+     * specified group, priority, daemon status, name,
+     * access control context and context class loader
+     */
+    public void testPrivilegedThreadFactory() throws Exception {
+        final CountDownLatch done = new CountDownLatch(1);
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                final ThreadGroup egroup = Thread.currentThread().getThreadGroup();
+                final ClassLoader thisccl = Thread.currentThread().getContextClassLoader();
+                // final AccessControlContext thisacc = AccessController.getContext(); // Android removed
+                Runnable r = new CheckedRunnable() {
+                    public void realRun() {
+                        Thread current = Thread.currentThread();
+                        assertTrue(!current.isDaemon());
+                        assertTrue(current.getPriority() <= Thread.NORM_PRIORITY);
+                        ThreadGroup g = current.getThreadGroup();
+                        SecurityManager s = System.getSecurityManager();
+                        if (s != null)
+                            assertTrue(g == s.getThreadGroup());
+                        else
+                            assertTrue(g == egroup);
+                        String name = current.getName();
+                        assertTrue(name.endsWith("thread-1"));
+                        assertSame(thisccl, current.getContextClassLoader());
+                        // assertEquals(thisacc, AccessController.getContext()); // Android removed
+                        done.countDown();
+                    }};
+                ExecutorService e = Executors.newSingleThreadExecutor(Executors.privilegedThreadFactory());
+                e.execute(r);
+                await(done);
+                e.shutdown();
+                joinPool(e);
+            }};
+
+        runWithPermissions(r,
+                           new RuntimePermission("getClassLoader"),
+                           new RuntimePermission("setContextClassLoader"),
+                           new RuntimePermission("modifyThread"));
+    }
+
+    boolean haveCCLPermissions() {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            try {
+                sm.checkPermission(new RuntimePermission("setContextClassLoader"));
+                sm.checkPermission(new RuntimePermission("getClassLoader"));
+            } catch (AccessControlException e) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    void checkCCL() {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(new RuntimePermission("setContextClassLoader"));
+            sm.checkPermission(new RuntimePermission("getClassLoader"));
+        }
+    }
+
+    class CheckCCL implements Callable<Object> {
+        public Object call() {
+            checkCCL();
+            return null;
+        }
+    }
+
+    /**
+     * Without class loader permissions, creating
+     * privilegedCallableUsingCurrentClassLoader throws ACE
+     */
+    public void testCreatePrivilegedCallableUsingCCLWithNoPrivs() {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                if (System.getSecurityManager() == null)
+                    return;
+                try {
+                    Executors.privilegedCallableUsingCurrentClassLoader(new NoOpCallable());
+                    shouldThrow();
+                } catch (AccessControlException success) {}
+            }};
+
+        runWithoutPermissions(r);
+    }
+
+    /**
+     * With class loader permissions, calling
+     * privilegedCallableUsingCurrentClassLoader does not throw ACE
+     */
+    public void testPrivilegedCallableUsingCCLWithPrivs() throws Exception {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                Executors.privilegedCallableUsingCurrentClassLoader
+                    (new NoOpCallable())
+                    .call();
+            }};
+
+        runWithPermissions(r,
+                           new RuntimePermission("getClassLoader"),
+                           new RuntimePermission("setContextClassLoader"));
+    }
+
+    /**
+     * Without permissions, calling privilegedCallable throws ACE
+     */
+    public void testPrivilegedCallableWithNoPrivs() throws Exception {
+        // Avoid classloader-related SecurityExceptions in swingui.TestRunner
+        Executors.privilegedCallable(new CheckCCL());
+
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                if (System.getSecurityManager() == null)
+                    return;
+                Callable task = Executors.privilegedCallable(new CheckCCL());
+                try {
+                    task.call();
+                    shouldThrow();
+                } catch (AccessControlException success) {}
+            }};
+
+        runWithoutPermissions(r);
+
+        // It seems rather difficult to test that the
+        // AccessControlContext of the privilegedCallable is used
+        // instead of its caller.  Below is a failed attempt to do
+        // that, which does not work because the AccessController
+        // cannot capture the internal state of the current Policy.
+        // It would be much more work to differentiate based on,
+        // e.g. CodeSource.
+
+//         final AccessControlContext[] noprivAcc = new AccessControlContext[1];
+//         final Callable[] task = new Callable[1];
+
+//         runWithPermissions
+//             (new CheckedRunnable() {
+//                 public void realRun() {
+//                     if (System.getSecurityManager() == null)
+//                         return;
+//                     noprivAcc[0] = AccessController.getContext();
+//                     task[0] = Executors.privilegedCallable(new CheckCCL());
+//                     try {
+//                         AccessController.doPrivileged(new PrivilegedAction<Void>() {
+//                                                           public Void run() {
+//                                                               checkCCL();
+//                                                               return null;
+//                                                           }}, noprivAcc[0]);
+//                         shouldThrow();
+//                     } catch (AccessControlException success) {}
+//                 }});
+
+//         runWithPermissions
+//             (new CheckedRunnable() {
+//                 public void realRun() throws Exception {
+//                     if (System.getSecurityManager() == null)
+//                         return;
+//                     // Verify that we have an underprivileged ACC
+//                     try {
+//                         AccessController.doPrivileged(new PrivilegedAction<Void>() {
+//                                                           public Void run() {
+//                                                               checkCCL();
+//                                                               return null;
+//                                                           }}, noprivAcc[0]);
+//                         shouldThrow();
+//                     } catch (AccessControlException success) {}
+
+//                     try {
+//                         task[0].call();
+//                         shouldThrow();
+//                     } catch (AccessControlException success) {}
+//                 }},
+//              new RuntimePermission("getClassLoader"),
+//              new RuntimePermission("setContextClassLoader"));
+    }
+
+    /**
+     * With permissions, calling privilegedCallable succeeds
+     */
+    public void testPrivilegedCallableWithPrivs() throws Exception {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                Executors.privilegedCallable(new CheckCCL()).call();
+            }};
+
+        runWithPermissions(r,
+                           new RuntimePermission("getClassLoader"),
+                           new RuntimePermission("setContextClassLoader"));
+    }
+
+    /**
+     * callable(Runnable) returns null when called
+     */
+    public void testCallable1() throws Exception {
+        Callable c = Executors.callable(new NoOpRunnable());
+        assertNull(c.call());
+    }
+
+    /**
+     * callable(Runnable, result) returns result when called
+     */
+    public void testCallable2() throws Exception {
+        Callable c = Executors.callable(new NoOpRunnable(), one);
+        assertSame(one, c.call());
+    }
+
+    /**
+     * callable(PrivilegedAction) returns its result when called
+     */
+    public void testCallable3() throws Exception {
+        Callable c = Executors.callable(new PrivilegedAction() {
+                public Object run() { return one; }});
+        assertSame(one, c.call());
+    }
+
+    /**
+     * callable(PrivilegedExceptionAction) returns its result when called
+     */
+    public void testCallable4() throws Exception {
+        Callable c = Executors.callable(new PrivilegedExceptionAction() {
+                public Object run() { return one; }});
+        assertSame(one, c.call());
+    }
+
+    /**
+     * callable(null Runnable) throws NPE
+     */
+    public void testCallableNPE1() {
+        try {
+            Callable c = Executors.callable((Runnable) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * callable(null, result) throws NPE
+     */
+    public void testCallableNPE2() {
+        try {
+            Callable c = Executors.callable((Runnable) null, one);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * callable(null PrivilegedAction) throws NPE
+     */
+    public void testCallableNPE3() {
+        try {
+            Callable c = Executors.callable((PrivilegedAction) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * callable(null PrivilegedExceptionAction) throws NPE
+     */
+    public void testCallableNPE4() {
+        try {
+            Callable c = Executors.callable((PrivilegedExceptionAction) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ForkJoinPoolTest.java b/jsr166-tests/src/test/java/jsr166/ForkJoinPoolTest.java
new file mode 100644
index 0000000..8416198
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ForkJoinPoolTest.java
@@ -0,0 +1,996 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.ForkJoinWorkerThread;
+import java.util.concurrent.RecursiveTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.ReentrantLock;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import java.security.AccessControlException;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+
+public class ForkJoinPoolTest extends JSR166TestCase {
+
+    /*
+     * Testing coverage notes:
+     *
+     * 1. shutdown and related methods are tested via super.joinPool.
+     *
+     * 2. newTaskFor and adapters are tested in submit/invoke tests
+     *
+     * 3. We cannot portably test monitoring methods such as
+     * getStealCount() since they rely ultimately on random task
+     * stealing that may cause tasks not to be stolen/propagated
+     * across threads, especially on uniprocessors.
+     *
+     * 4. There are no independently testable ForkJoinWorkerThread
+     * methods, but they are covered here and in task tests.
+     */
+
+    // Some classes to test extension and factory methods
+
+    static class MyHandler implements Thread.UncaughtExceptionHandler {
+        volatile int catches = 0;
+        public void uncaughtException(Thread t, Throwable e) {
+            ++catches;
+        }
+    }
+
+    // to test handlers
+    static class FailingFJWSubclass extends ForkJoinWorkerThread {
+        public FailingFJWSubclass(ForkJoinPool p) { super(p) ; }
+        protected void onStart() { super.onStart(); throw new Error(); }
+    }
+
+    static class FailingThreadFactory
+            implements ForkJoinPool.ForkJoinWorkerThreadFactory {
+        volatile int calls = 0;
+        public ForkJoinWorkerThread newThread(ForkJoinPool p) {
+            if (++calls > 1) return null;
+            return new FailingFJWSubclass(p);
+        }
+    }
+
+    static class SubFJP extends ForkJoinPool { // to expose protected
+        SubFJP() { super(1); }
+        public int drainTasksTo(Collection<? super ForkJoinTask<?>> c) {
+            return super.drainTasksTo(c);
+        }
+        public ForkJoinTask<?> pollSubmission() {
+            return super.pollSubmission();
+        }
+    }
+
+    static class ManagedLocker implements ForkJoinPool.ManagedBlocker {
+        final ReentrantLock lock;
+        boolean hasLock = false;
+        ManagedLocker(ReentrantLock lock) { this.lock = lock; }
+        public boolean block() {
+            if (!hasLock)
+                lock.lock();
+            return true;
+        }
+        public boolean isReleasable() {
+            return hasLock || (hasLock = lock.tryLock());
+        }
+    }
+
+    // A simple recursive task for testing
+    static final class FibTask extends RecursiveTask<Integer> {
+        final int number;
+        FibTask(int n) { number = n; }
+        public Integer compute() {
+            int n = number;
+            if (n <= 1)
+                return n;
+            FibTask f1 = new FibTask(n - 1);
+            f1.fork();
+            return (new FibTask(n - 2)).compute() + f1.join();
+        }
+    }
+
+    // A failing task for testing
+    static final class FailingTask extends ForkJoinTask<Void> {
+        public final Void getRawResult() { return null; }
+        protected final void setRawResult(Void mustBeNull) { }
+        protected final boolean exec() { throw new Error(); }
+        FailingTask() {}
+    }
+
+    // Fib needlessly using locking to test ManagedBlockers
+    static final class LockingFibTask extends RecursiveTask<Integer> {
+        final int number;
+        final ManagedLocker locker;
+        final ReentrantLock lock;
+        LockingFibTask(int n, ManagedLocker locker, ReentrantLock lock) {
+            number = n;
+            this.locker = locker;
+            this.lock = lock;
+        }
+        public Integer compute() {
+            int n;
+            LockingFibTask f1 = null;
+            LockingFibTask f2 = null;
+            locker.block();
+            n = number;
+            if (n > 1) {
+                f1 = new LockingFibTask(n - 1, locker, lock);
+                f2 = new LockingFibTask(n - 2, locker, lock);
+            }
+            lock.unlock();
+            if (n <= 1)
+                return n;
+            else {
+                f1.fork();
+                return f2.compute() + f1.join();
+            }
+        }
+    }
+
+    /**
+     * Successfully constructed pool reports default factory,
+     * parallelism and async mode policies, no active threads or
+     * tasks, and quiescent running state.
+     */
+    public void testDefaultInitialState() {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try {
+            assertSame(ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                       p.getFactory());
+            assertFalse(p.getAsyncMode());
+            assertEquals(0, p.getActiveThreadCount());
+            assertEquals(0, p.getStealCount());
+            assertEquals(0, p.getQueuedTaskCount());
+            assertEquals(0, p.getQueuedSubmissionCount());
+            assertFalse(p.hasQueuedSubmissions());
+            assertFalse(p.isShutdown());
+            assertFalse(p.isTerminating());
+            assertFalse(p.isTerminated());
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * Constructor throws if size argument is less than zero
+     */
+    public void testConstructor1() {
+        try {
+            new ForkJoinPool(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if factory argument is null
+     */
+    public void testConstructor2() {
+        try {
+            new ForkJoinPool(1, null, null, false);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getParallelism returns size set in constructor
+     */
+    public void testGetParallelism() {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try {
+            assertEquals(1, p.getParallelism());
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getPoolSize returns number of started workers.
+     */
+    public void testGetPoolSize() {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try {
+            assertEquals(0, p.getActiveThreadCount());
+            Future<String> future = p.submit(new StringTask());
+            assertEquals(1, p.getPoolSize());
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * awaitTermination on a non-shutdown pool times out
+     */
+    public void testAwaitTermination_timesOut() throws InterruptedException {
+        ForkJoinPool p = new ForkJoinPool(1);
+        assertFalse(p.isTerminated());
+        assertFalse(p.awaitTermination(Long.MIN_VALUE, NANOSECONDS));
+        assertFalse(p.awaitTermination(Long.MIN_VALUE, MILLISECONDS));
+        assertFalse(p.awaitTermination(-1L, NANOSECONDS));
+        assertFalse(p.awaitTermination(-1L, MILLISECONDS));
+        assertFalse(p.awaitTermination(0L, NANOSECONDS));
+        assertFalse(p.awaitTermination(0L, MILLISECONDS));
+        long timeoutNanos = 999999L;
+        long startTime = System.nanoTime();
+        assertFalse(p.awaitTermination(timeoutNanos, NANOSECONDS));
+        assertTrue(System.nanoTime() - startTime >= timeoutNanos);
+        assertFalse(p.isTerminated());
+        startTime = System.nanoTime();
+        long timeoutMillis = timeoutMillis();
+        assertFalse(p.awaitTermination(timeoutMillis, MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+        assertFalse(p.isTerminated());
+        p.shutdown();
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+    }
+
+    /**
+     * setUncaughtExceptionHandler changes handler for uncaught exceptions.
+     *
+     * Additionally tests: Overriding ForkJoinWorkerThread.onStart
+     * performs its defined action
+     */
+    public void testSetUncaughtExceptionHandler() throws InterruptedException {
+        final CountDownLatch uehInvoked = new CountDownLatch(1);
+        final Thread.UncaughtExceptionHandler eh =
+            new Thread.UncaughtExceptionHandler() {
+                public void uncaughtException(Thread t, Throwable e) {
+                    uehInvoked.countDown();
+                }};
+        ForkJoinPool p = new ForkJoinPool(1, new FailingThreadFactory(),
+                                          eh, false);
+        try {
+            assertSame(eh, p.getUncaughtExceptionHandler());
+            try {
+                p.execute(new FibTask(8));
+                assertTrue(uehInvoked.await(MEDIUM_DELAY_MS, MILLISECONDS));
+            } catch (RejectedExecutionException ok) {
+            }
+        } finally {
+            p.shutdownNow(); // failure might have prevented processing task
+            joinPool(p);
+        }
+    }
+
+    /**
+     * After invoking a single task, isQuiescent eventually becomes
+     * true, at which time queues are empty, threads are not active,
+     * the task has completed successfully, and construction
+     * parameters continue to hold
+     */
+    public void testIsQuiescent() throws Exception {
+        ForkJoinPool p = new ForkJoinPool(2);
+        try {
+            assertTrue(p.isQuiescent());
+            long startTime = System.nanoTime();
+            FibTask f = new FibTask(20);
+            p.invoke(f);
+            assertSame(ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                       p.getFactory());
+            while (! p.isQuiescent()) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    throw new AssertionFailedError("timed out");
+                assertFalse(p.getAsyncMode());
+                assertFalse(p.isShutdown());
+                assertFalse(p.isTerminating());
+                assertFalse(p.isTerminated());
+                Thread.yield();
+            }
+
+            assertTrue(p.isQuiescent());
+            assertFalse(p.getAsyncMode());
+            assertEquals(0, p.getActiveThreadCount());
+            assertEquals(0, p.getQueuedTaskCount());
+            assertEquals(0, p.getQueuedSubmissionCount());
+            assertFalse(p.hasQueuedSubmissions());
+            assertFalse(p.isShutdown());
+            assertFalse(p.isTerminating());
+            assertFalse(p.isTerminated());
+            assertTrue(f.isDone());
+            assertEquals(6765, (int) f.get());
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * Completed submit(ForkJoinTask) returns result
+     */
+    public void testSubmitForkJoinTask() throws Throwable {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try {
+            ForkJoinTask<Integer> f = p.submit(new FibTask(8));
+            assertEquals(21, (int) f.get());
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * A task submitted after shutdown is rejected
+     */
+    public void testSubmitAfterShutdown() {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try {
+            p.shutdown();
+            assertTrue(p.isShutdown());
+            try {
+                ForkJoinTask<Integer> f = p.submit(new FibTask(8));
+                shouldThrow();
+            } catch (RejectedExecutionException success) {}
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * Pool maintains parallelism when using ManagedBlocker
+     */
+    public void testBlockingForkJoinTask() throws Throwable {
+        ForkJoinPool p = new ForkJoinPool(4);
+        try {
+            ReentrantLock lock = new ReentrantLock();
+            ManagedLocker locker = new ManagedLocker(lock);
+            ForkJoinTask<Integer> f = new LockingFibTask(20, locker, lock);
+            p.execute(f);
+            assertEquals(6765, (int) f.get());
+        } finally {
+            p.shutdownNow(); // don't wait out shutdown
+        }
+    }
+
+    /**
+     * pollSubmission returns unexecuted submitted task, if present
+     */
+    public void testPollSubmission() {
+        final CountDownLatch done = new CountDownLatch(1);
+        SubFJP p = new SubFJP();
+        try {
+            ForkJoinTask a = p.submit(awaiter(done));
+            ForkJoinTask b = p.submit(awaiter(done));
+            ForkJoinTask c = p.submit(awaiter(done));
+            ForkJoinTask r = p.pollSubmission();
+            assertTrue(r == a || r == b || r == c);
+            assertFalse(r.isDone());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * drainTasksTo transfers unexecuted submitted tasks, if present
+     */
+    public void testDrainTasksTo() {
+        final CountDownLatch done = new CountDownLatch(1);
+        SubFJP p = new SubFJP();
+        try {
+            ForkJoinTask a = p.submit(awaiter(done));
+            ForkJoinTask b = p.submit(awaiter(done));
+            ForkJoinTask c = p.submit(awaiter(done));
+            ArrayList<ForkJoinTask> al = new ArrayList();
+            p.drainTasksTo(al);
+            assertTrue(al.size() > 0);
+            for (ForkJoinTask r : al) {
+                assertTrue(r == a || r == b || r == c);
+                assertFalse(r.isDone());
+            }
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    // FJ Versions of AbstractExecutorService tests
+
+    /**
+     * execute(runnable) runs it to completion
+     */
+    public void testExecuteRunnable() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            final AtomicBoolean done = new AtomicBoolean(false);
+            CheckedRunnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.set(true);
+                }};
+            Future<?> future = e.submit(task);
+            assertNull(future.get());
+            assertNull(future.get(0, MILLISECONDS));
+            assertTrue(done.get());
+            assertTrue(future.isDone());
+            assertFalse(future.isCancelled());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * Completed submit(callable) returns result
+     */
+    public void testSubmitCallable() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            Future<String> future = e.submit(new StringTask());
+            assertSame(TEST_STRING, future.get());
+            assertTrue(future.isDone());
+            assertFalse(future.isCancelled());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * Completed submit(runnable) returns successfully
+     */
+    public void testSubmitRunnable() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            Future<?> future = e.submit(new NoOpRunnable());
+            assertNull(future.get());
+            assertTrue(future.isDone());
+            assertFalse(future.isCancelled());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * Completed submit(runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+            assertSame(TEST_STRING, future.get());
+            assertTrue(future.isDone());
+            assertFalse(future.isCancelled());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * A submitted privileged action runs to completion
+     */
+    public void testSubmitPrivilegedAction() throws Exception {
+        final Callable callable = Executors.callable(new PrivilegedAction() {
+                public Object run() { return TEST_STRING; }});
+        Runnable r = new CheckedRunnable() {
+        public void realRun() throws Exception {
+            ExecutorService e = new ForkJoinPool(1);
+            try {
+                Future future = e.submit(callable);
+                assertSame(TEST_STRING, future.get());
+            } finally {
+                joinPool(e);
+            }
+        }};
+
+        runWithPermissions(r, new RuntimePermission("modifyThread"));
+    }
+
+    /**
+     * A submitted privileged exception action runs to completion
+     */
+    public void testSubmitPrivilegedExceptionAction() throws Exception {
+        final Callable callable =
+            Executors.callable(new PrivilegedExceptionAction() {
+                public Object run() { return TEST_STRING; }});
+        Runnable r = new CheckedRunnable() {
+        public void realRun() throws Exception {
+            ExecutorService e = new ForkJoinPool(1);
+            try {
+                Future future = e.submit(callable);
+                assertSame(TEST_STRING, future.get());
+            } finally {
+                joinPool(e);
+            }
+        }};
+
+        runWithPermissions(r, new RuntimePermission("modifyThread"));
+    }
+
+    /**
+     * A submitted failed privileged exception action reports exception
+     */
+    public void testSubmitFailedPrivilegedExceptionAction() throws Exception {
+        final Callable callable =
+            Executors.callable(new PrivilegedExceptionAction() {
+                public Object run() { throw new IndexOutOfBoundsException(); }});
+        Runnable r = new CheckedRunnable() {
+        public void realRun() throws Exception {
+            ExecutorService e = new ForkJoinPool(1);
+            try {
+                Future future = e.submit(callable);
+                try {
+                    future.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    assertTrue(success.getCause() instanceof IndexOutOfBoundsException);
+                }
+            } finally {
+                joinPool(e);
+            }
+        }};
+
+        runWithPermissions(r, new RuntimePermission("modifyThread"));
+    }
+
+    /**
+     * execute(null runnable) throws NullPointerException
+     */
+    public void testExecuteNullRunnable() {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            Future<?> future = e.submit((Runnable) null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * submit(null callable) throws NullPointerException
+     */
+    public void testSubmitNullCallable() {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            Future<String> future = e.submit((Callable) null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * submit(callable).get() throws InterruptedException if interrupted
+     */
+    public void testInterruptedSubmit() throws InterruptedException {
+        final CountDownLatch submitted    = new CountDownLatch(1);
+        final CountDownLatch quittingTime = new CountDownLatch(1);
+        final ExecutorService p = new ForkJoinPool(1);
+        final Callable<Void> awaiter = new CheckedCallable<Void>() {
+            public Void realCall() throws InterruptedException {
+                assertTrue(quittingTime.await(MEDIUM_DELAY_MS, MILLISECONDS));
+                return null;
+            }};
+        try {
+            Thread t = new Thread(new CheckedInterruptedRunnable() {
+                public void realRun() throws Exception {
+                    Future<Void> future = p.submit(awaiter);
+                    submitted.countDown();
+                    future.get();
+                }});
+            t.start();
+            assertTrue(submitted.await(MEDIUM_DELAY_MS, MILLISECONDS));
+            t.interrupt();
+            t.join();
+        } finally {
+            quittingTime.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * get of submit(callable) throws ExecutionException if callable
+     * throws exception
+     */
+    public void testSubmitEE() throws Throwable {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try {
+            p.submit(new Callable() {
+                public Object call() { throw new ArithmeticException(); }})
+                .get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof ArithmeticException);
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NullPointerException
+     */
+    public void testInvokeAny1() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            e.invokeAny(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IllegalArgumentException
+     */
+    public void testInvokeAny2() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            e.invokeAny(new ArrayList<Callable<String>>());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NullPointerException if c has a single null element
+     */
+    public void testInvokeAny3() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(null);
+        try {
+            e.invokeAny(l);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NullPointerException if c has null elements
+     */
+    public void testInvokeAny4() throws Throwable {
+        CountDownLatch latch = new CountDownLatch(1);
+        ExecutorService e = new ForkJoinPool(1);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(latchAwaitingStringTask(latch));
+        l.add(null);
+        try {
+            e.invokeAny(l);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            latch.countDown();
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task in c completes
+     */
+    public void testInvokeAny5() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        try {
+            e.invokeAny(l);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task in c if at least one completes
+     */
+    public void testInvokeAny6() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NullPointerException
+     */
+    public void testInvokeAll1() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            e.invokeAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws InterruptedException {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            List<Future<String>> r
+                = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NullPointerException if c has null elements
+     */
+    public void testInvokeAll3() throws InterruptedException {
+        ExecutorService e = new ForkJoinPool(1);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        l.add(null);
+        try {
+            e.invokeAll(l);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * get of returned element of invokeAll(c) throws
+     * ExecutionException on failed task
+     */
+    public void testInvokeAll4() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        List<Future<String>> futures = e.invokeAll(l);
+        assertEquals(1, futures.size());
+        try {
+            futures.get(0).get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks in c
+     */
+    public void testInvokeAll5() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NullPointerException
+     */
+    public void testTimedInvokeAny1() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(null time unit) throws NullPointerException
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IllegalArgumentException
+     */
+    public void testTimedInvokeAny2() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            e.invokeAny(new ArrayList<Callable<String>>(),
+                        MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NullPointerException if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Throwable {
+        CountDownLatch latch = new CountDownLatch(1);
+        ExecutorService e = new ForkJoinPool(1);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(latchAwaitingStringTask(latch));
+        l.add(null);
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            latch.countDown();
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task in c
+     */
+    public void testTimedInvokeAny5() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NullPointerException
+     */
+    public void testTimedInvokeAll1() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(null time unit) throws NullPointerException
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        try {
+            e.invokeAll(l, MEDIUM_DELAY_MS, null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws InterruptedException {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            List<Future<String>> r
+                = e.invokeAll(new ArrayList<Callable<String>>(),
+                              MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NullPointerException if c has null elements
+     */
+    public void testTimedInvokeAll3() throws InterruptedException {
+        ExecutorService e = new ForkJoinPool(1);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        l.add(null);
+        try {
+            e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * get of returned element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        List<Future<String>> futures
+            = e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+        assertEquals(1, futures.size());
+        try {
+            futures.get(0).get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks in c
+     */
+    public void testTimedInvokeAll5() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures
+                = e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ForkJoinTaskTest.java b/jsr166-tests/src/test/java/jsr166/ForkJoinTaskTest.java
new file mode 100644
index 0000000..080fd9c
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ForkJoinTaskTest.java
@@ -0,0 +1,1605 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.ForkJoinWorkerThread;
+import java.util.concurrent.RecursiveAction;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import java.util.HashSet;
+import junit.framework.*;
+
+public class ForkJoinTaskTest extends JSR166TestCase {
+
+    // Runs with "mainPool" use > 1 thread. singletonPool tests use 1
+    static final int mainPoolSize =
+        Math.max(2, Runtime.getRuntime().availableProcessors());
+
+    private static ForkJoinPool mainPool() {
+        return new ForkJoinPool(mainPoolSize);
+    }
+
+    private static ForkJoinPool singletonPool() {
+        return new ForkJoinPool(1);
+    }
+
+    private static ForkJoinPool asyncSingletonPool() {
+        return new ForkJoinPool(1,
+                                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                                null, true);
+    }
+
+    private void testInvokeOnPool(ForkJoinPool pool, RecursiveAction a) {
+        try {
+            assertFalse(a.isDone());
+            assertFalse(a.isCompletedNormally());
+            assertFalse(a.isCompletedAbnormally());
+            assertFalse(a.isCancelled());
+            assertNull(a.getException());
+            assertNull(a.getRawResult());
+
+            assertNull(pool.invoke(a));
+
+            assertTrue(a.isDone());
+            assertTrue(a.isCompletedNormally());
+            assertFalse(a.isCompletedAbnormally());
+            assertFalse(a.isCancelled());
+            assertNull(a.getException());
+            assertNull(a.getRawResult());
+        } finally {
+            joinPool(pool);
+        }
+    }
+
+    void checkNotDone(ForkJoinTask a) {
+        assertFalse(a.isDone());
+        assertFalse(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertFalse(a.isCancelled());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        try {
+            a.get(0L, SECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    <T> void checkCompletedNormally(ForkJoinTask<T> a) {
+        checkCompletedNormally(a, null);
+    }
+
+    <T> void checkCompletedNormally(ForkJoinTask<T> a, T expected) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertTrue(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertNull(a.getException());
+        assertSame(expected, a.getRawResult());
+
+        {
+            Thread.currentThread().interrupt();
+            long t0 = System.nanoTime();
+            assertSame(expected, a.join());
+            assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+            Thread.interrupted();
+        }
+
+        {
+            Thread.currentThread().interrupt();
+            long t0 = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+            Thread.interrupted();
+        }
+
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        try {
+            assertSame(expected, a.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertSame(expected, a.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCancelled(ForkJoinTask a) {
+        assertTrue(a.isDone());
+        assertTrue(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertTrue(a.getException() instanceof CancellationException);
+        assertNull(a.getRawResult());
+        assertTrue(a.cancel(false));
+        assertTrue(a.cancel(true));
+
+        try {
+            Thread.currentThread().interrupt();
+            a.join();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        Thread.interrupted();
+
+        {
+            long t0 = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedAbnormally(ForkJoinTask a, Throwable t) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertSame(t.getClass(), a.getException().getClass());
+        assertNull(a.getRawResult());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+
+        try {
+            Thread.currentThread().interrupt();
+            a.join();
+            shouldThrow();
+        } catch (Throwable expected) {
+            assertSame(t.getClass(), expected.getClass());
+        }
+        Thread.interrupted();
+
+        {
+            long t0 = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    /*
+     * Testing coverage notes:
+     *
+     * To test extension methods and overrides, most tests use
+     * BinaryAsyncAction extension class that processes joins
+     * differently than supplied Recursive forms.
+     */
+
+    public static final class FJException extends RuntimeException {
+        FJException() { super(); }
+    }
+
+    abstract static class BinaryAsyncAction extends ForkJoinTask<Void> {
+        private volatile int controlState;
+
+        static final AtomicIntegerFieldUpdater<BinaryAsyncAction> controlStateUpdater =
+            AtomicIntegerFieldUpdater.newUpdater(BinaryAsyncAction.class,
+                                                 "controlState");
+
+        private BinaryAsyncAction parent;
+
+        private BinaryAsyncAction sibling;
+
+        protected BinaryAsyncAction() {
+        }
+
+        public final Void getRawResult() { return null; }
+        protected final void setRawResult(Void mustBeNull) { }
+
+        public final void linkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) {
+            x.parent = y.parent = this;
+            x.sibling = y;
+            y.sibling = x;
+        }
+
+        protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+        }
+
+        protected boolean onException() {
+            return true;
+        }
+
+        public void linkAndForkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) {
+            linkSubtasks(x, y);
+            y.fork();
+            x.fork();
+        }
+
+        private void completeThis() {
+            super.complete(null);
+        }
+
+        private void completeThisExceptionally(Throwable ex) {
+            super.completeExceptionally(ex);
+        }
+
+        public final void complete() {
+            BinaryAsyncAction a = this;
+            for (;;) {
+                BinaryAsyncAction s = a.sibling;
+                BinaryAsyncAction p = a.parent;
+                a.sibling = null;
+                a.parent = null;
+                a.completeThis();
+                if (p == null || p.compareAndSetControlState(0, 1))
+                    break;
+                try {
+                    p.onComplete(a, s);
+                } catch (Throwable rex) {
+                    p.completeExceptionally(rex);
+                    return;
+                }
+                a = p;
+            }
+        }
+
+        public final void completeExceptionally(Throwable ex) {
+            BinaryAsyncAction a = this;
+            while (!a.isCompletedAbnormally()) {
+                a.completeThisExceptionally(ex);
+                BinaryAsyncAction s = a.sibling;
+                if (s != null)
+                    s.cancel(false);
+                if (!a.onException() || (a = a.parent) == null)
+                    break;
+            }
+        }
+
+        public final BinaryAsyncAction getParent() {
+            return parent;
+        }
+
+        public BinaryAsyncAction getSibling() {
+            return sibling;
+        }
+
+        public void reinitialize() {
+            parent = sibling = null;
+            super.reinitialize();
+        }
+
+        protected final int getControlState() {
+            return controlState;
+        }
+
+        protected final boolean compareAndSetControlState(int expect,
+                                                          int update) {
+            return controlStateUpdater.compareAndSet(this, expect, update);
+        }
+
+        protected final void setControlState(int value) {
+            controlState = value;
+        }
+
+        protected final void incrementControlState() {
+            controlStateUpdater.incrementAndGet(this);
+        }
+
+        protected final void decrementControlState() {
+            controlStateUpdater.decrementAndGet(this);
+        }
+
+    }
+
+    static final class AsyncFib extends BinaryAsyncAction {
+        int number;
+        public AsyncFib(int n) {
+            this.number = n;
+        }
+
+        public final boolean exec() {
+            AsyncFib f = this;
+            int n = f.number;
+            if (n > 1) {
+                while (n > 1) {
+                    AsyncFib p = f;
+                    AsyncFib r = new AsyncFib(n - 2);
+                    f = new AsyncFib(--n);
+                    p.linkSubtasks(r, f);
+                    r.fork();
+                }
+                f.number = n;
+            }
+            f.complete();
+            return false;
+        }
+
+        protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+            number = ((AsyncFib)x).number + ((AsyncFib)y).number;
+        }
+    }
+
+    static final class FailingAsyncFib extends BinaryAsyncAction {
+        int number;
+        public FailingAsyncFib(int n) {
+            this.number = n;
+        }
+
+        public final boolean exec() {
+            FailingAsyncFib f = this;
+            int n = f.number;
+            if (n > 1) {
+                while (n > 1) {
+                    FailingAsyncFib p = f;
+                    FailingAsyncFib r = new FailingAsyncFib(n - 2);
+                    f = new FailingAsyncFib(--n);
+                    p.linkSubtasks(r, f);
+                    r.fork();
+                }
+                f.number = n;
+            }
+            f.complete();
+            return false;
+        }
+
+        protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+            completeExceptionally(new FJException());
+        }
+    }
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks; getRawResult returns null.
+     */
+    public void testInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPE() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesce() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                assertEquals(0, getQueuedTaskCount());
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGet() throws Exception {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * getPool of executing task returns its pool
+     */
+    public void testGetPool() {
+        final ForkJoinPool mainPool = mainPool();
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertSame(mainPool, getPool());
+            }};
+        testInvokeOnPool(mainPool, a);
+    }
+
+    /**
+     * getPool of non-FJ task returns null
+     */
+    public void testGetPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertNull(getPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * inForkJoinPool of executing task returns true
+     */
+    public void testInForkJoinPool() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertTrue(inForkJoinPool());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertFalse(inForkJoinPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * setRawResult(null) succeeds
+     */
+    public void testSetRawResult() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                setRawResult(null);
+                assertNull(getRawResult());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.completeExceptionally(new FJException());
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                invokeAll(f, g);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.number);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                invokeAll(f, g, h);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollection() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPE() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection)  throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollection() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * tryUnfork returns true for most recent unexecuted task,
+     * and suppresses execution
+     */
+    public void testTryUnfork() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertTrue(f.tryUnfork());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * getSurplusQueuedTaskCount returns > 0 when
+     * there are more tasks than threads
+     */
+    public void testGetSurplusQueuedTaskCount() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib h = new AsyncFib(7);
+                assertSame(h, h.fork());
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertTrue(getSurplusQueuedTaskCount() > 0);
+                helpQuiesce();
+                assertEquals(0, getSurplusQueuedTaskCount());
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns most recent unexecuted task.
+     */
+    public void testPeekNextLocalTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(f, peekNextLocalTask());
+                assertNull(f.join());
+                checkCompletedNormally(f);
+                helpQuiesce();
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns most recent unexecuted task without
+     * executing it
+     */
+    public void testPollNextLocalTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollNextLocalTask());
+                helpQuiesce();
+                checkNotDone(f);
+                assertEquals(34, g.number);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it
+     */
+    public void testPollTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns least recent unexecuted task in async mode
+     */
+    public void testPeekNextLocalTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(g, peekNextLocalTask());
+                assertNull(f.join());
+                helpQuiesce();
+                checkCompletedNormally(f);
+                assertEquals(34, g.number);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns least recent unexecuted task without
+     * executing it, in async mode
+     */
+    public void testPollNextLocalTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollNextLocalTask());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it, in
+     * async mode
+     */
+    public void testPollTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollTask());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    // versions for singleton pools
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks; getRawResult returns null.
+     */
+    public void testInvokeSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvokeSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGetSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGetSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPESingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesceSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                assertEquals(0, getQueuedTaskCount());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvokeSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvokeSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGetSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGetSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvokeSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGetSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGetSingleton() throws Exception {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionallySingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.completeExceptionally(new FJException());
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                invokeAll(f, g);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.number);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                invokeAll(f, g, h);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollectionSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPESingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(collection)  throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollectionSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/FutureTaskTest.java b/jsr166-tests/src/test/java/jsr166/FutureTaskTest.java
new file mode 100644
index 0000000..baab79e
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/FutureTaskTest.java
@@ -0,0 +1,799 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import java.util.*;
+
+public class FutureTaskTest extends JSR166TestCase {
+
+    void checkIsDone(Future<?> f) {
+        assertTrue(f.isDone());
+        assertFalse(f.cancel(false));
+        assertFalse(f.cancel(true));
+        if (f instanceof PublicFutureTask) {
+            PublicFutureTask pf = (PublicFutureTask) f;
+            assertEquals(1, pf.doneCount());
+            assertFalse(pf.runAndReset());
+            assertEquals(1, pf.doneCount());
+            Object r = null; Object exInfo = null;
+            try {
+                r = f.get();
+            } catch (CancellationException t) {
+                exInfo = CancellationException.class;
+            } catch (ExecutionException t) {
+                exInfo = t.getCause();
+            } catch (Throwable t) {
+                threadUnexpectedException(t);
+            }
+
+            // Check that run and runAndReset have no effect.
+            int savedRunCount = pf.runCount();
+            pf.run();
+            pf.runAndReset();
+            assertEquals(savedRunCount, pf.runCount());
+            try {
+                assertSame(r, f.get());
+            } catch (CancellationException t) {
+                assertSame(exInfo, CancellationException.class);
+            } catch (ExecutionException t) {
+                assertSame(exInfo, t.getCause());
+            } catch (Throwable t) {
+                threadUnexpectedException(t);
+            }
+            assertTrue(f.isDone());
+        }
+    }
+
+    void checkNotDone(Future<?> f) {
+        assertFalse(f.isDone());
+        assertFalse(f.isCancelled());
+        if (f instanceof PublicFutureTask) {
+            PublicFutureTask pf = (PublicFutureTask) f;
+            assertEquals(0, pf.doneCount());
+            assertEquals(0, pf.setCount());
+            assertEquals(0, pf.setExceptionCount());
+        }
+    }
+
+    void checkIsRunning(Future<?> f) {
+        checkNotDone(f);
+        if (f instanceof FutureTask) {
+            FutureTask ft = (FutureTask<?>) f;
+            // Check that run methods do nothing
+            ft.run();
+            if (f instanceof PublicFutureTask) {
+                PublicFutureTask pf = (PublicFutureTask) f;
+                int savedRunCount = pf.runCount();
+                pf.run();
+                assertFalse(pf.runAndReset());
+                assertEquals(savedRunCount, pf.runCount());
+            }
+            checkNotDone(f);
+        }
+    }
+
+    <T> void checkCompletedNormally(Future<T> f, T expected) {
+        checkIsDone(f);
+        assertFalse(f.isCancelled());
+
+        try {
+            assertSame(expected, f.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertSame(expected, f.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCancelled(Future<?> f) {
+        checkIsDone(f);
+        assertTrue(f.isCancelled());
+
+        try {
+            f.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            f.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void tryToConfuseDoneTask(PublicFutureTask pf) {
+        pf.set(new Object());
+        pf.setException(new Error());
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false }) {
+            pf.cancel(true);
+        }
+    }
+
+    void checkCompletedAbnormally(Future<?> f, Throwable t) {
+        checkIsDone(f);
+        assertFalse(f.isCancelled());
+
+        try {
+            f.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t, success.getCause());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            f.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t, success.getCause());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    /**
+     * Subclass to expose protected methods
+     */
+    static class PublicFutureTask extends FutureTask {
+        private final AtomicInteger runCount;
+        private final AtomicInteger doneCount = new AtomicInteger(0);
+        private final AtomicInteger runAndResetCount = new AtomicInteger(0);
+        private final AtomicInteger setCount = new AtomicInteger(0);
+        private final AtomicInteger setExceptionCount = new AtomicInteger(0);
+        public int runCount() { return runCount.get(); }
+        public int doneCount() { return doneCount.get(); }
+        public int runAndResetCount() { return runAndResetCount.get(); }
+        public int setCount() { return setCount.get(); }
+        public int setExceptionCount() { return setExceptionCount.get(); }
+
+        PublicFutureTask(Runnable runnable) {
+            this(runnable, seven);
+        }
+        PublicFutureTask(Runnable runnable, Object result) {
+            this(runnable, result, new AtomicInteger(0));
+        }
+        private PublicFutureTask(final Runnable runnable, Object result,
+                                 final AtomicInteger runCount) {
+            super(new Runnable() {
+                public void run() {
+                    runCount.getAndIncrement();
+                    runnable.run();
+                }}, result);
+            this.runCount = runCount;
+        }
+        PublicFutureTask(Callable callable) {
+            this(callable, new AtomicInteger(0));
+        }
+        private PublicFutureTask(final Callable callable,
+                                 final AtomicInteger runCount) {
+            super(new Callable() {
+                public Object call() throws Exception {
+                    runCount.getAndIncrement();
+                    return callable.call();
+                }});
+            this.runCount = runCount;
+        }
+        @Override public void done() {
+            assertTrue(isDone());
+            doneCount.incrementAndGet();
+            super.done();
+        }
+        @Override public boolean runAndReset() {
+            runAndResetCount.incrementAndGet();
+            return super.runAndReset();
+        }
+        @Override public void set(Object x) {
+            setCount.incrementAndGet();
+            super.set(x);
+        }
+        @Override public void setException(Throwable t) {
+            setExceptionCount.incrementAndGet();
+            super.setException(t);
+        }
+    }
+
+    class Counter extends CheckedRunnable {
+        final AtomicInteger count = new AtomicInteger(0);
+        public int get() { return count.get(); }
+        public void realRun() {
+            count.getAndIncrement();
+        }
+    }
+
+    /**
+     * creating a future with a null callable throws NullPointerException
+     */
+    public void testConstructor() {
+        try {
+            new FutureTask(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * creating a future with null runnable throws NullPointerException
+     */
+    public void testConstructor2() {
+        try {
+            new FutureTask(null, Boolean.TRUE);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * isDone is true when a task completes
+     */
+    public void testIsDone() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        assertFalse(task.isDone());
+        task.run();
+        assertTrue(task.isDone());
+        checkCompletedNormally(task, Boolean.TRUE);
+        assertEquals(1, task.runCount());
+    }
+
+    /**
+     * runAndReset of a non-cancelled task succeeds
+     */
+    public void testRunAndReset() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        for (int i = 0; i < 3; i++) {
+            assertTrue(task.runAndReset());
+            checkNotDone(task);
+            assertEquals(i+1, task.runCount());
+            assertEquals(i+1, task.runAndResetCount());
+            assertEquals(0, task.setCount());
+            assertEquals(0, task.setExceptionCount());
+        }
+    }
+
+    /**
+     * runAndReset after cancellation fails
+     */
+    public void testRunAndResetAfterCancel() {
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false }) {
+            PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+            assertTrue(task.cancel(mayInterruptIfRunning));
+            for (int i = 0; i < 3; i++) {
+                assertFalse(task.runAndReset());
+                assertEquals(0, task.runCount());
+                assertEquals(i+1, task.runAndResetCount());
+                assertEquals(0, task.setCount());
+                assertEquals(0, task.setExceptionCount());
+            }
+            tryToConfuseDoneTask(task);
+            checkCancelled(task);
+        }
+    }
+
+    /**
+     * setting value causes get to return it
+     */
+    public void testSet() throws Exception {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        task.set(one);
+        for (int i = 0; i < 3; i++) {
+            assertSame(one, task.get());
+            assertSame(one, task.get(LONG_DELAY_MS, MILLISECONDS));
+            assertEquals(1, task.setCount());
+        }
+        tryToConfuseDoneTask(task);
+        checkCompletedNormally(task, one);
+        assertEquals(0, task.runCount());
+    }
+
+    /**
+     * setException causes get to throw ExecutionException
+     */
+    public void testSetException_get() throws Exception {
+        Exception nse = new NoSuchElementException();
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        task.setException(nse);
+
+        try {
+            task.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(nse, success.getCause());
+            checkCompletedAbnormally(task, nse);
+        }
+
+        try {
+            task.get(LONG_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(nse, success.getCause());
+            checkCompletedAbnormally(task, nse);
+        }
+
+        assertEquals(1, task.setExceptionCount());
+        assertEquals(0, task.setCount());
+        tryToConfuseDoneTask(task);
+        checkCompletedAbnormally(task, nse);
+        assertEquals(0, task.runCount());
+    }
+
+    /**
+     * cancel(false) before run succeeds
+     */
+    public void testCancelBeforeRun() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        assertTrue(task.cancel(false));
+        task.run();
+        assertEquals(0, task.runCount());
+        assertEquals(0, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        assertTrue(task.isCancelled());
+        assertTrue(task.isDone());
+        tryToConfuseDoneTask(task);
+        assertEquals(0, task.runCount());
+        checkCancelled(task);
+    }
+
+    /**
+     * cancel(true) before run succeeds
+     */
+    public void testCancelBeforeRun2() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        assertTrue(task.cancel(true));
+        task.run();
+        assertEquals(0, task.runCount());
+        assertEquals(0, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        assertTrue(task.isCancelled());
+        assertTrue(task.isDone());
+        tryToConfuseDoneTask(task);
+        assertEquals(0, task.runCount());
+        checkCancelled(task);
+    }
+
+    /**
+     * cancel(false) of a completed task fails
+     */
+    public void testCancelAfterRun() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        task.run();
+        assertFalse(task.cancel(false));
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCompletedNormally(task, Boolean.TRUE);
+        assertEquals(1, task.runCount());
+    }
+
+    /**
+     * cancel(true) of a completed task fails
+     */
+    public void testCancelAfterRun2() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        task.run();
+        assertFalse(task.cancel(true));
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCompletedNormally(task, Boolean.TRUE);
+        assertEquals(1, task.runCount());
+    }
+
+    /**
+     * cancel(true) interrupts a running task that subsequently succeeds
+     */
+    public void testCancelInterrupt() {
+        final CountDownLatch pleaseCancel = new CountDownLatch(1);
+        final PublicFutureTask task =
+            new PublicFutureTask(new CheckedRunnable() {
+                public void realRun() {
+                    pleaseCancel.countDown();
+                    try {
+                        delay(LONG_DELAY_MS);
+                        shouldThrow();
+                    } catch (InterruptedException success) {}
+                }});
+
+        Thread t = newStartedThread(task);
+        await(pleaseCancel);
+        assertTrue(task.cancel(true));
+        assertTrue(task.isCancelled());
+        assertTrue(task.isDone());
+        awaitTermination(t);
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCancelled(task);
+    }
+
+    /**
+     * cancel(true) tries to interrupt a running task, but
+     * Thread.interrupt throws (simulating a restrictive security
+     * manager)
+     */
+    public void testCancelInterrupt_ThrowsSecurityException() {
+        final CountDownLatch pleaseCancel = new CountDownLatch(1);
+        final CountDownLatch cancelled = new CountDownLatch(1);
+        final PublicFutureTask task =
+            new PublicFutureTask(new CheckedRunnable() {
+                public void realRun() {
+                    pleaseCancel.countDown();
+                    await(cancelled);
+                    assertFalse(Thread.interrupted());
+                }});
+
+        final Thread t = new Thread(task) {
+            // Simulate a restrictive security manager.
+            @Override public void interrupt() {
+                throw new SecurityException();
+            }};
+        t.setDaemon(true);
+        t.start();
+
+        await(pleaseCancel);
+        try {
+            task.cancel(true);
+            shouldThrow();
+        } catch (SecurityException expected) {}
+
+        // We failed to deliver the interrupt, but the world retains
+        // its sanity, as if we had done task.cancel(false)
+        assertTrue(task.isCancelled());
+        assertTrue(task.isDone());
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.doneCount());
+        assertEquals(0, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        cancelled.countDown();
+        awaitTermination(t);
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCancelled(task);
+    }
+
+    /**
+     * cancel(true) interrupts a running task that subsequently throws
+     */
+    public void testCancelInterrupt_taskFails() {
+        final CountDownLatch pleaseCancel = new CountDownLatch(1);
+        final PublicFutureTask task =
+            new PublicFutureTask(new Runnable() {
+                public void run() {
+                    try {
+                        pleaseCancel.countDown();
+                        delay(LONG_DELAY_MS);
+                        threadShouldThrow();
+                    } catch (InterruptedException success) {
+                    } catch (Throwable t) { threadUnexpectedException(t); }
+                    throw new RuntimeException();
+                }});
+
+        Thread t = newStartedThread(task);
+        await(pleaseCancel);
+        assertTrue(task.cancel(true));
+        assertTrue(task.isCancelled());
+        awaitTermination(t);
+        assertEquals(1, task.runCount());
+        assertEquals(0, task.setCount());
+        assertEquals(1, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCancelled(task);
+    }
+
+    /**
+     * cancel(false) does not interrupt a running task
+     */
+    public void testCancelNoInterrupt() {
+        final CountDownLatch pleaseCancel = new CountDownLatch(1);
+        final CountDownLatch cancelled = new CountDownLatch(1);
+        final PublicFutureTask task =
+            new PublicFutureTask(new CheckedCallable<Boolean>() {
+                public Boolean realCall() {
+                    pleaseCancel.countDown();
+                    await(cancelled);
+                    assertFalse(Thread.interrupted());
+                    return Boolean.TRUE;
+                }});
+
+        Thread t = newStartedThread(task);
+        await(pleaseCancel);
+        assertTrue(task.cancel(false));
+        assertTrue(task.isCancelled());
+        cancelled.countDown();
+        awaitTermination(t);
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCancelled(task);
+    }
+
+    /**
+     * run in one thread causes get in another thread to retrieve value
+     */
+    public void testGetRun() {
+        final CountDownLatch pleaseRun = new CountDownLatch(2);
+
+        final PublicFutureTask task =
+            new PublicFutureTask(new CheckedCallable<Object>() {
+                public Object realCall() {
+                    return two;
+                }});
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                pleaseRun.countDown();
+                assertSame(two, task.get());
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                pleaseRun.countDown();
+                assertSame(two, task.get(2*LONG_DELAY_MS, MILLISECONDS));
+            }});
+
+        await(pleaseRun);
+        checkNotDone(task);
+        assertTrue(t1.isAlive());
+        assertTrue(t2.isAlive());
+        task.run();
+        checkCompletedNormally(task, two);
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        awaitTermination(t1);
+        awaitTermination(t2);
+        tryToConfuseDoneTask(task);
+        checkCompletedNormally(task, two);
+    }
+
+    /**
+     * set in one thread causes get in another thread to retrieve value
+     */
+    public void testGetSet() {
+        final CountDownLatch pleaseSet = new CountDownLatch(2);
+
+        final PublicFutureTask task =
+            new PublicFutureTask(new CheckedCallable<Object>() {
+                public Object realCall() throws InterruptedException {
+                    return two;
+                }});
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                pleaseSet.countDown();
+                assertSame(two, task.get());
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                pleaseSet.countDown();
+                assertSame(two, task.get(2*LONG_DELAY_MS, MILLISECONDS));
+            }});
+
+        await(pleaseSet);
+        checkNotDone(task);
+        assertTrue(t1.isAlive());
+        assertTrue(t2.isAlive());
+        task.set(two);
+        assertEquals(0, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCompletedNormally(task, two);
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Cancelling a task causes timed get in another thread to throw
+     * CancellationException
+     */
+    public void testTimedGet_Cancellation() {
+        testTimedGet_Cancellation(false);
+    }
+    public void testTimedGet_Cancellation_interrupt() {
+        testTimedGet_Cancellation(true);
+    }
+    public void testTimedGet_Cancellation(final boolean mayInterruptIfRunning) {
+        final CountDownLatch pleaseCancel = new CountDownLatch(3);
+        final CountDownLatch cancelled = new CountDownLatch(1);
+        final Callable<Object> callable =
+            new CheckedCallable<Object>() {
+            public Object realCall() throws InterruptedException {
+                pleaseCancel.countDown();
+                if (mayInterruptIfRunning) {
+                    try {
+                        delay(2*LONG_DELAY_MS);
+                    } catch (InterruptedException success) {}
+                } else {
+                    await(cancelled);
+                }
+                return two;
+            }};
+        final PublicFutureTask task = new PublicFutureTask(callable);
+
+        Thread t1 = new ThreadShouldThrow(CancellationException.class) {
+                public void realRun() throws Exception {
+                    pleaseCancel.countDown();
+                    task.get();
+                }};
+        Thread t2 = new ThreadShouldThrow(CancellationException.class) {
+                public void realRun() throws Exception {
+                    pleaseCancel.countDown();
+                    task.get(2*LONG_DELAY_MS, MILLISECONDS);
+                }};
+        t1.start();
+        t2.start();
+        Thread t3 = newStartedThread(task);
+        await(pleaseCancel);
+        checkIsRunning(task);
+        task.cancel(mayInterruptIfRunning);
+        checkCancelled(task);
+        awaitTermination(t1);
+        awaitTermination(t2);
+        cancelled.countDown();
+        awaitTermination(t3);
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCancelled(task);
+    }
+
+    /**
+     * A runtime exception in task causes get to throw ExecutionException
+     */
+    public void testGet_ExecutionException() throws InterruptedException {
+        final ArithmeticException e = new ArithmeticException();
+        final PublicFutureTask task = new PublicFutureTask(new Callable() {
+            public Object call() {
+                throw e;
+            }});
+
+        task.run();
+        assertEquals(1, task.runCount());
+        assertEquals(0, task.setCount());
+        assertEquals(1, task.setExceptionCount());
+        try {
+            task.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(e, success.getCause());
+            tryToConfuseDoneTask(task);
+            checkCompletedAbnormally(task, success.getCause());
+        }
+    }
+
+    /**
+     * A runtime exception in task causes timed get to throw ExecutionException
+     */
+    public void testTimedGet_ExecutionException2() throws Exception {
+        final ArithmeticException e = new ArithmeticException();
+        final PublicFutureTask task = new PublicFutureTask(new Callable() {
+            public Object call() {
+                throw e;
+            }});
+
+        task.run();
+        try {
+            task.get(LONG_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(e, success.getCause());
+            tryToConfuseDoneTask(task);
+            checkCompletedAbnormally(task, success.getCause());
+        }
+    }
+
+    /**
+     * get is interruptible
+     */
+    public void testGet_interruptible() {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        final FutureTask task = new FutureTask(new NoOpCallable());
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                Thread.currentThread().interrupt();
+                try {
+                    task.get();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    task.get();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        t.interrupt();
+        awaitTermination(t);
+        checkNotDone(task);
+    }
+
+    /**
+     * timed get is interruptible
+     */
+    public void testTimedGet_interruptible() {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        final FutureTask task = new FutureTask(new NoOpCallable());
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                Thread.currentThread().interrupt();
+                try {
+                    task.get(2*LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    task.get(2*LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        t.interrupt();
+        awaitTermination(t);
+        checkNotDone(task);
+    }
+
+    /**
+     * A timed out timed get throws TimeoutException
+     */
+    public void testGet_TimeoutException() throws Exception {
+        FutureTask task = new FutureTask(new NoOpCallable());
+        long startTime = System.nanoTime();
+        try {
+            task.get(timeoutMillis(), MILLISECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        }
+    }
+
+    /**
+     * timed get with null TimeUnit throws NullPointerException
+     */
+    public void testGet_NullTimeUnit() throws Exception {
+        FutureTask task = new FutureTask(new NoOpCallable());
+        long[] timeouts = { Long.MIN_VALUE, 0L, Long.MAX_VALUE };
+
+        for (long timeout : timeouts) {
+            try {
+                task.get(timeout, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+
+        task.run();
+
+        for (long timeout : timeouts) {
+            try {
+                task.get(timeout, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java b/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java
new file mode 100644
index 0000000..d9bf255
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java
@@ -0,0 +1,1140 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.PropertyPermission;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.security.SecurityPermission;
+
+/**
+ * Base class for JSR166 Junit TCK tests.  Defines some constants,
+ * utility methods and classes, as well as a simple framework for
+ * helping to make sure that assertions failing in generated threads
+ * cause the associated test that generated them to itself fail (which
+ * JUnit does not otherwise arrange).  The rules for creating such
+ * tests are:
+ *
+ * <ol>
+ *
+ * <li> All assertions in code running in generated threads must use
+ * the forms {@link #threadFail}, {@link #threadAssertTrue}, {@link
+ * #threadAssertEquals}, or {@link #threadAssertNull}, (not
+ * {@code fail}, {@code assertTrue}, etc.) It is OK (but not
+ * particularly recommended) for other code to use these forms too.
+ * Only the most typically used JUnit assertion methods are defined
+ * this way, but enough to live with.</li>
+ *
+ * <li> If you override {@link #setUp} or {@link #tearDown}, make sure
+ * to invoke {@code super.setUp} and {@code super.tearDown} within
+ * them. These methods are used to clear and check for thread
+ * assertion failures.</li>
+ *
+ * <li>All delays and timeouts must use one of the constants {@code
+ * SHORT_DELAY_MS}, {@code SMALL_DELAY_MS}, {@code MEDIUM_DELAY_MS},
+ * {@code LONG_DELAY_MS}. The idea here is that a SHORT is always
+ * discriminable from zero time, and always allows enough time for the
+ * small amounts of computation (creating a thread, calling a few
+ * methods, etc) needed to reach a timeout point. Similarly, a SMALL
+ * is always discriminable as larger than SHORT and smaller than
+ * MEDIUM.  And so on. These constants are set to conservative values,
+ * but even so, if there is ever any doubt, they can all be increased
+ * in one spot to rerun tests on slower platforms.</li>
+ *
+ * <li> All threads generated must be joined inside each test case
+ * method (or {@code fail} to do so) before returning from the
+ * method. The {@code joinPool} method can be used to do this when
+ * using Executors.</li>
+ *
+ * </ol>
+ *
+ * <p><b>Other notes</b>
+ * <ul>
+ *
+ * <li> Usually, there is one testcase method per JSR166 method
+ * covering "normal" operation, and then as many exception-testing
+ * methods as there are exceptions the method can throw. Sometimes
+ * there are multiple tests per JSR166 method when the different
+ * "normal" behaviors differ significantly. And sometimes testcases
+ * cover multiple methods when they cannot be tested in
+ * isolation.</li>
+ *
+ * <li> The documentation style for testcases is to provide as javadoc
+ * a simple sentence or two describing the property that the testcase
+ * method purports to test. The javadocs do not say anything about how
+ * the property is tested. To find out, read the code.</li>
+ *
+ * <li> These tests are "conformance tests", and do not attempt to
+ * test throughput, latency, scalability or other performance factors
+ * (see the separate "jtreg" tests for a set intended to check these
+ * for the most central aspects of functionality.) So, most tests use
+ * the smallest sensible numbers of threads, collection sizes, etc
+ * needed to check basic conformance.</li>
+ *
+ * <li>The test classes currently do not declare inclusion in
+ * any particular package to simplify things for people integrating
+ * them in TCK test suites.</li>
+ *
+ * <li> As a convenience, the {@code main} of this class (JSR166TestCase)
+ * runs all JSR166 unit tests.</li>
+ *
+ * </ul>
+ */
+public class JSR166TestCase extends TestCase {
+
+    protected static final boolean expensiveTests = false;
+
+    public static long SHORT_DELAY_MS;
+    public static long SMALL_DELAY_MS;
+    public static long MEDIUM_DELAY_MS;
+    public static long LONG_DELAY_MS;
+
+
+    /**
+     * Returns the shortest timed delay. This could
+     * be reimplemented to use for example a Property.
+     */
+    protected long getShortDelay() {
+        return 50;
+    }
+
+    /**
+     * Sets delays as multiples of SHORT_DELAY.
+     */
+    protected void setDelays() {
+        SHORT_DELAY_MS = getShortDelay();
+        SMALL_DELAY_MS  = SHORT_DELAY_MS * 5;
+        MEDIUM_DELAY_MS = SHORT_DELAY_MS * 10;
+        LONG_DELAY_MS   = SHORT_DELAY_MS * 200;
+    }
+
+    /**
+     * Returns a timeout in milliseconds to be used in tests that
+     * verify that operations block or time out.
+     */
+    long timeoutMillis() {
+        return SHORT_DELAY_MS / 4;
+    }
+
+    /**
+     * Returns a new Date instance representing a time delayMillis
+     * milliseconds in the future.
+     */
+    Date delayedDate(long delayMillis) {
+        return new Date(System.currentTimeMillis() + delayMillis);
+    }
+
+    /**
+     * The first exception encountered if any threadAssertXXX method fails.
+     */
+    private final AtomicReference<Throwable> threadFailure
+        = new AtomicReference<Throwable>(null);
+
+    /**
+     * Records an exception so that it can be rethrown later in the test
+     * harness thread, triggering a test case failure.  Only the first
+     * failure is recorded; subsequent calls to this method from within
+     * the same test have no effect.
+     */
+    public void threadRecordFailure(Throwable t) {
+        threadFailure.compareAndSet(null, t);
+    }
+
+    public void setUp() {
+        setDelays();
+    }
+
+    /**
+     * Extra checks that get done for all test cases.
+     *
+     * Triggers test case failure if any thread assertions have failed,
+     * by rethrowing, in the test harness thread, any exception recorded
+     * earlier by threadRecordFailure.
+     *
+     * Triggers test case failure if interrupt status is set in the main thread.
+     */
+    public void tearDown() throws Exception {
+        Throwable t = threadFailure.getAndSet(null);
+        if (t != null) {
+            if (t instanceof Error)
+                throw (Error) t;
+            else if (t instanceof RuntimeException)
+                throw (RuntimeException) t;
+            else if (t instanceof Exception)
+                throw (Exception) t;
+            else {
+                AssertionFailedError afe =
+                    new AssertionFailedError(t.toString());
+                afe.initCause(t);
+                throw afe;
+            }
+        }
+
+        if (Thread.interrupted())
+            throw new AssertionFailedError("interrupt status set in main thread");
+
+        checkForkJoinPoolThreadLeaks();
+    }
+
+    /**
+     * Find missing try { ... } finally { joinPool(e); }
+     */
+    void checkForkJoinPoolThreadLeaks() throws InterruptedException {
+        Thread[] survivors = new Thread[5];
+        int count = Thread.enumerate(survivors);
+        for (int i = 0; i < count; i++) {
+            Thread thread = survivors[i];
+            String name = thread.getName();
+            if (name.startsWith("ForkJoinPool-")) {
+                // give thread some time to terminate
+                thread.join(LONG_DELAY_MS);
+                if (!thread.isAlive()) continue;
+                thread.stop();
+                throw new AssertionFailedError
+                    (String.format("Found leaked ForkJoinPool thread test=%s thread=%s%n",
+                                   toString(), name));
+            }
+        }
+    }
+
+    /**
+     * Just like fail(reason), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadFail(String reason) {
+        try {
+            fail(reason);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            fail(reason);
+        }
+    }
+
+    /**
+     * Just like assertTrue(b), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertTrue(boolean b) {
+        try {
+            assertTrue(b);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            throw t;
+        }
+    }
+
+    /**
+     * Just like assertFalse(b), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertFalse(boolean b) {
+        try {
+            assertFalse(b);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            throw t;
+        }
+    }
+
+    /**
+     * Just like assertNull(x), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertNull(Object x) {
+        try {
+            assertNull(x);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            throw t;
+        }
+    }
+
+    /**
+     * Just like assertEquals(x, y), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertEquals(long x, long y) {
+        try {
+            assertEquals(x, y);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            throw t;
+        }
+    }
+
+    /**
+     * Just like assertEquals(x, y), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertEquals(Object x, Object y) {
+        try {
+            assertEquals(x, y);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            throw t;
+        } catch (Throwable t) {
+            threadUnexpectedException(t);
+        }
+    }
+
+    /**
+     * Just like assertSame(x, y), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertSame(Object x, Object y) {
+        try {
+            assertSame(x, y);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            throw t;
+        }
+    }
+
+    /**
+     * Calls threadFail with message "should throw exception".
+     */
+    public void threadShouldThrow() {
+        threadFail("should throw exception");
+    }
+
+    /**
+     * Calls threadFail with message "should throw" + exceptionName.
+     */
+    public void threadShouldThrow(String exceptionName) {
+        threadFail("should throw " + exceptionName);
+    }
+
+    /**
+     * Records the given exception using {@link #threadRecordFailure},
+     * then rethrows the exception, wrapping it in an
+     * AssertionFailedError if necessary.
+     */
+    public void threadUnexpectedException(Throwable t) {
+        threadRecordFailure(t);
+        t.printStackTrace();
+        if (t instanceof RuntimeException)
+            throw (RuntimeException) t;
+        else if (t instanceof Error)
+            throw (Error) t;
+        else {
+            AssertionFailedError afe =
+                new AssertionFailedError("unexpected exception: " + t);
+            afe.initCause(t);
+            throw afe;
+        }
+    }
+
+    /**
+     * Delays, via Thread.sleep, for the given millisecond delay, but
+     * if the sleep is shorter than specified, may re-sleep or yield
+     * until time elapses.
+     */
+    static void delay(long millis) throws InterruptedException {
+        long startTime = System.nanoTime();
+        long ns = millis * 1000 * 1000;
+        for (;;) {
+            if (millis > 0L)
+                Thread.sleep(millis);
+            else // too short to sleep
+                Thread.yield();
+            long d = ns - (System.nanoTime() - startTime);
+            if (d > 0L)
+                millis = d / (1000 * 1000);
+            else
+                break;
+        }
+    }
+
+    /**
+     * Waits out termination of a thread pool or fails doing so.
+     */
+    void joinPool(ExecutorService exec) {
+        try {
+            exec.shutdown();
+            assertTrue("ExecutorService did not terminate in a timely manner",
+                       exec.awaitTermination(2 * LONG_DELAY_MS, MILLISECONDS));
+        } catch (SecurityException ok) {
+            // Allowed in case test doesn't have privs
+        } catch (InterruptedException ie) {
+            fail("Unexpected InterruptedException");
+        }
+    }
+
+    /**
+     * Checks that thread does not terminate within the default
+     * millisecond delay of {@code timeoutMillis()}.
+     */
+    void assertThreadStaysAlive(Thread thread) {
+        assertThreadStaysAlive(thread, timeoutMillis());
+    }
+
+    /**
+     * Checks that thread does not terminate within the given millisecond delay.
+     */
+    void assertThreadStaysAlive(Thread thread, long millis) {
+        try {
+            // No need to optimize the failing case via Thread.join.
+            delay(millis);
+            assertTrue(thread.isAlive());
+        } catch (InterruptedException ie) {
+            fail("Unexpected InterruptedException");
+        }
+    }
+
+    /**
+     * Checks that the threads do not terminate within the default
+     * millisecond delay of {@code timeoutMillis()}.
+     */
+    void assertThreadsStayAlive(Thread... threads) {
+        assertThreadsStayAlive(timeoutMillis(), threads);
+    }
+
+    /**
+     * Checks that the threads do not terminate within the given millisecond delay.
+     */
+    void assertThreadsStayAlive(long millis, Thread... threads) {
+        try {
+            // No need to optimize the failing case via Thread.join.
+            delay(millis);
+            for (Thread thread : threads)
+                assertTrue(thread.isAlive());
+        } catch (InterruptedException ie) {
+            fail("Unexpected InterruptedException");
+        }
+    }
+
+    /**
+     * Checks that future.get times out, with the default timeout of
+     * {@code timeoutMillis()}.
+     */
+    void assertFutureTimesOut(Future future) {
+        assertFutureTimesOut(future, timeoutMillis());
+    }
+
+    /**
+     * Checks that future.get times out, with the given millisecond timeout.
+     */
+    void assertFutureTimesOut(Future future, long timeoutMillis) {
+        long startTime = System.nanoTime();
+        try {
+            future.get(timeoutMillis, MILLISECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Exception e) {
+            threadUnexpectedException(e);
+        } finally { future.cancel(true); }
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+    }
+
+    /**
+     * Fails with message "should throw exception".
+     */
+    public void shouldThrow() {
+        fail("Should throw exception");
+    }
+
+    /**
+     * Fails with message "should throw " + exceptionName.
+     */
+    public void shouldThrow(String exceptionName) {
+        fail("Should throw " + exceptionName);
+    }
+
+    /**
+     * The number of elements to place in collections, arrays, etc.
+     */
+    public static final int SIZE = 20;
+
+    // Some convenient Integer constants
+
+    public static final Integer zero  = new Integer(0);
+    public static final Integer one   = new Integer(1);
+    public static final Integer two   = new Integer(2);
+    public static final Integer three = new Integer(3);
+    public static final Integer four  = new Integer(4);
+    public static final Integer five  = new Integer(5);
+    public static final Integer six   = new Integer(6);
+    public static final Integer seven = new Integer(7);
+    public static final Integer eight = new Integer(8);
+    public static final Integer nine  = new Integer(9);
+    public static final Integer m1  = new Integer(-1);
+    public static final Integer m2  = new Integer(-2);
+    public static final Integer m3  = new Integer(-3);
+    public static final Integer m4  = new Integer(-4);
+    public static final Integer m5  = new Integer(-5);
+    public static final Integer m6  = new Integer(-6);
+    public static final Integer m10 = new Integer(-10);
+
+
+    /**
+     * android-changed
+     * Android does not use a SecurityManager. This will simply execute
+     * the runnable ingoring permisions.
+     */
+    public void runWithPermissions(Runnable r, Permission... permissions) {
+        r.run();
+    }
+
+    /**
+     * android-changed
+     * Android does not use a SecurityManager. This will simply execute
+     * the runnable ingoring permisions.
+     */
+    public void runWithSecurityManagerWithPermissions(Runnable r,
+                                                      Permission... permissions) {
+        r.run();
+    }
+
+    /**
+     * Runs a runnable without any permissions.
+     */
+    public void runWithoutPermissions(Runnable r) {
+        runWithPermissions(r);
+    }
+
+    /**
+     * A security policy where new permissions can be dynamically added
+     * or all cleared.
+     */
+    public static class AdjustablePolicy extends java.security.Policy {
+        Permissions perms = new Permissions();
+        AdjustablePolicy(Permission... permissions) {
+            for (Permission permission : permissions)
+                perms.add(permission);
+        }
+        void addPermission(Permission perm) { perms.add(perm); }
+        void clearPermissions() { perms = new Permissions(); }
+        public PermissionCollection getPermissions(CodeSource cs) {
+            return perms;
+        }
+        public PermissionCollection getPermissions(ProtectionDomain pd) {
+            return perms;
+        }
+        public boolean implies(ProtectionDomain pd, Permission p) {
+            return perms.implies(p);
+        }
+        public void refresh() {}
+        public String toString() {
+            List<Permission> ps = new ArrayList<Permission>();
+            for (Enumeration<Permission> e = perms.elements(); e.hasMoreElements();)
+                ps.add(e.nextElement());
+            return "AdjustablePolicy with permissions " + ps;
+        }
+    }
+
+    /**
+     * Returns a policy containing all the permissions we ever need.
+     */
+    public static Policy permissivePolicy() {
+        return new AdjustablePolicy
+            // Permissions j.u.c. needs directly
+            (new RuntimePermission("modifyThread"),
+             new RuntimePermission("getClassLoader"),
+             new RuntimePermission("setContextClassLoader"),
+             // Permissions needed to change permissions!
+             new SecurityPermission("getPolicy"),
+             new SecurityPermission("setPolicy"),
+             new RuntimePermission("setSecurityManager"),
+             // Permissions needed by the junit test harness
+             new RuntimePermission("accessDeclaredMembers"),
+             new PropertyPermission("*", "read"),
+             new java.io.FilePermission("<<ALL FILES>>", "read"));
+    }
+
+    /**
+     * Sleeps until the given time has elapsed.
+     * Throws AssertionFailedError if interrupted.
+     */
+    void sleep(long millis) {
+        try {
+            delay(millis);
+        } catch (InterruptedException ie) {
+            AssertionFailedError afe =
+                new AssertionFailedError("Unexpected InterruptedException");
+            afe.initCause(ie);
+            throw afe;
+        }
+    }
+
+    /**
+     * Spin-waits up to the specified number of milliseconds for the given
+     * thread to enter a wait state: BLOCKED, WAITING, or TIMED_WAITING.
+     */
+    void waitForThreadToEnterWaitState(Thread thread, long timeoutMillis) {
+        long startTime = System.nanoTime();
+        for (;;) {
+            Thread.State s = thread.getState();
+            if (s == Thread.State.BLOCKED ||
+                s == Thread.State.WAITING ||
+                s == Thread.State.TIMED_WAITING)
+                return;
+            else if (s == Thread.State.TERMINATED)
+                fail("Unexpected thread termination");
+            else if (millisElapsedSince(startTime) > timeoutMillis) {
+                threadAssertTrue(thread.isAlive());
+                return;
+            }
+            Thread.yield();
+        }
+    }
+
+    /**
+     * Waits up to LONG_DELAY_MS for the given thread to enter a wait
+     * state: BLOCKED, WAITING, or TIMED_WAITING.
+     */
+    void waitForThreadToEnterWaitState(Thread thread) {
+        waitForThreadToEnterWaitState(thread, LONG_DELAY_MS);
+    }
+
+    /**
+     * Returns the number of milliseconds since time given by
+     * startNanoTime, which must have been previously returned from a
+     * call to {@link System.nanoTime()}.
+     */
+    long millisElapsedSince(long startNanoTime) {
+        return NANOSECONDS.toMillis(System.nanoTime() - startNanoTime);
+    }
+
+    /**
+     * Returns a new started daemon Thread running the given runnable.
+     */
+    Thread newStartedThread(Runnable runnable) {
+        Thread t = new Thread(runnable);
+        t.setDaemon(true);
+        t.start();
+        return t;
+    }
+
+    /**
+     * Waits for the specified time (in milliseconds) for the thread
+     * to terminate (using {@link Thread#join(long)}), else interrupts
+     * the thread (in the hope that it may terminate later) and fails.
+     */
+    void awaitTermination(Thread t, long timeoutMillis) {
+        try {
+            t.join(timeoutMillis);
+        } catch (InterruptedException ie) {
+            threadUnexpectedException(ie);
+        } finally {
+            if (t.getState() != Thread.State.TERMINATED) {
+                t.interrupt();
+                fail("Test timed out");
+            }
+        }
+    }
+
+    /**
+     * Waits for LONG_DELAY_MS milliseconds for the thread to
+     * terminate (using {@link Thread#join(long)}), else interrupts
+     * the thread (in the hope that it may terminate later) and fails.
+     */
+    void awaitTermination(Thread t) {
+        awaitTermination(t, LONG_DELAY_MS);
+    }
+
+    // Some convenient Runnable classes
+
+    public abstract class CheckedRunnable implements Runnable {
+        protected abstract void realRun() throws Throwable;
+
+        public final void run() {
+            try {
+                realRun();
+            } catch (Throwable t) {
+                threadUnexpectedException(t);
+            }
+        }
+    }
+
+    public abstract class RunnableShouldThrow implements Runnable {
+        protected abstract void realRun() throws Throwable;
+
+        final Class<?> exceptionClass;
+
+        <T extends Throwable> RunnableShouldThrow(Class<T> exceptionClass) {
+            this.exceptionClass = exceptionClass;
+        }
+
+        public final void run() {
+            try {
+                realRun();
+                threadShouldThrow(exceptionClass.getSimpleName());
+            } catch (Throwable t) {
+                if (! exceptionClass.isInstance(t))
+                    threadUnexpectedException(t);
+            }
+        }
+    }
+
+    public abstract class ThreadShouldThrow extends Thread {
+        protected abstract void realRun() throws Throwable;
+
+        final Class<?> exceptionClass;
+
+        <T extends Throwable> ThreadShouldThrow(Class<T> exceptionClass) {
+            this.exceptionClass = exceptionClass;
+        }
+
+        public final void run() {
+            try {
+                realRun();
+                threadShouldThrow(exceptionClass.getSimpleName());
+            } catch (Throwable t) {
+                if (! exceptionClass.isInstance(t))
+                    threadUnexpectedException(t);
+            }
+        }
+    }
+
+    public abstract class CheckedInterruptedRunnable implements Runnable {
+        protected abstract void realRun() throws Throwable;
+
+        public final void run() {
+            try {
+                realRun();
+                threadShouldThrow("InterruptedException");
+            } catch (InterruptedException success) {
+                threadAssertFalse(Thread.interrupted());
+            } catch (Throwable t) {
+                threadUnexpectedException(t);
+            }
+        }
+    }
+
+    public abstract class CheckedCallable<T> implements Callable<T> {
+        protected abstract T realCall() throws Throwable;
+
+        public final T call() {
+            try {
+                return realCall();
+            } catch (Throwable t) {
+                threadUnexpectedException(t);
+                return null;
+            }
+        }
+    }
+
+    public abstract class CheckedInterruptedCallable<T>
+        implements Callable<T> {
+        protected abstract T realCall() throws Throwable;
+
+        public final T call() {
+            try {
+                T result = realCall();
+                threadShouldThrow("InterruptedException");
+                return result;
+            } catch (InterruptedException success) {
+                threadAssertFalse(Thread.interrupted());
+            } catch (Throwable t) {
+                threadUnexpectedException(t);
+            }
+            return null;
+        }
+    }
+
+    public static class NoOpRunnable implements Runnable {
+        public void run() {}
+    }
+
+    public static class NoOpCallable implements Callable {
+        public Object call() { return Boolean.TRUE; }
+    }
+
+    public static final String TEST_STRING = "a test string";
+
+    public static class StringTask implements Callable<String> {
+        public String call() { return TEST_STRING; }
+    }
+
+    public Callable<String> latchAwaitingStringTask(final CountDownLatch latch) {
+        return new CheckedCallable<String>() {
+            protected String realCall() {
+                try {
+                    latch.await();
+                } catch (InterruptedException quittingTime) {}
+                return TEST_STRING;
+            }};
+    }
+
+    public Runnable awaiter(final CountDownLatch latch) {
+        return new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                await(latch);
+            }};
+    }
+
+    public void await(CountDownLatch latch) {
+        try {
+            assertTrue(latch.await(LONG_DELAY_MS, MILLISECONDS));
+        } catch (Throwable t) {
+            threadUnexpectedException(t);
+        }
+    }
+
+    public void await(Semaphore semaphore) {
+        try {
+            assertTrue(semaphore.tryAcquire(LONG_DELAY_MS, MILLISECONDS));
+        } catch (Throwable t) {
+            threadUnexpectedException(t);
+        }
+    }
+
+//     /**
+//      * Spin-waits up to LONG_DELAY_MS until flag becomes true.
+//      */
+//     public void await(AtomicBoolean flag) {
+//         await(flag, LONG_DELAY_MS);
+//     }
+
+//     /**
+//      * Spin-waits up to the specified timeout until flag becomes true.
+//      */
+//     public void await(AtomicBoolean flag, long timeoutMillis) {
+//         long startTime = System.nanoTime();
+//         while (!flag.get()) {
+//             if (millisElapsedSince(startTime) > timeoutMillis)
+//                 throw new AssertionFailedError("timed out");
+//             Thread.yield();
+//         }
+//     }
+
+    public static class NPETask implements Callable<String> {
+        public String call() { throw new NullPointerException(); }
+    }
+
+    public static class CallableOne implements Callable<Integer> {
+        public Integer call() { return one; }
+    }
+
+    public class ShortRunnable extends CheckedRunnable {
+        protected void realRun() throws Throwable {
+            delay(SHORT_DELAY_MS);
+        }
+    }
+
+    public class ShortInterruptedRunnable extends CheckedInterruptedRunnable {
+        protected void realRun() throws InterruptedException {
+            delay(SHORT_DELAY_MS);
+        }
+    }
+
+    public class SmallRunnable extends CheckedRunnable {
+        protected void realRun() throws Throwable {
+            delay(SMALL_DELAY_MS);
+        }
+    }
+
+    public class SmallPossiblyInterruptedRunnable extends CheckedRunnable {
+        protected void realRun() {
+            try {
+                delay(SMALL_DELAY_MS);
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public class SmallCallable extends CheckedCallable {
+        protected Object realCall() throws InterruptedException {
+            delay(SMALL_DELAY_MS);
+            return Boolean.TRUE;
+        }
+    }
+
+    public class MediumRunnable extends CheckedRunnable {
+        protected void realRun() throws Throwable {
+            delay(MEDIUM_DELAY_MS);
+        }
+    }
+
+    public class MediumInterruptedRunnable extends CheckedInterruptedRunnable {
+        protected void realRun() throws InterruptedException {
+            delay(MEDIUM_DELAY_MS);
+        }
+    }
+
+    public Runnable possiblyInterruptedRunnable(final long timeoutMillis) {
+        return new CheckedRunnable() {
+            protected void realRun() {
+                try {
+                    delay(timeoutMillis);
+                } catch (InterruptedException ok) {}
+            }};
+    }
+
+    public class MediumPossiblyInterruptedRunnable extends CheckedRunnable {
+        protected void realRun() {
+            try {
+                delay(MEDIUM_DELAY_MS);
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public class LongPossiblyInterruptedRunnable extends CheckedRunnable {
+        protected void realRun() {
+            try {
+                delay(LONG_DELAY_MS);
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    /**
+     * For use as ThreadFactory in constructors
+     */
+    public static class SimpleThreadFactory implements ThreadFactory {
+        public Thread newThread(Runnable r) {
+            return new Thread(r);
+        }
+    }
+
+    public interface TrackedRunnable extends Runnable {
+        boolean isDone();
+    }
+
+    public static TrackedRunnable trackedRunnable(final long timeoutMillis) {
+        return new TrackedRunnable() {
+                private volatile boolean done = false;
+                public boolean isDone() { return done; }
+                public void run() {
+                    try {
+                        delay(timeoutMillis);
+                        done = true;
+                    } catch (InterruptedException ok) {}
+                }
+            };
+    }
+
+    public static class TrackedShortRunnable implements Runnable {
+        public volatile boolean done = false;
+        public void run() {
+            try {
+                delay(SHORT_DELAY_MS);
+                done = true;
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public static class TrackedSmallRunnable implements Runnable {
+        public volatile boolean done = false;
+        public void run() {
+            try {
+                delay(SMALL_DELAY_MS);
+                done = true;
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public static class TrackedMediumRunnable implements Runnable {
+        public volatile boolean done = false;
+        public void run() {
+            try {
+                delay(MEDIUM_DELAY_MS);
+                done = true;
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public static class TrackedLongRunnable implements Runnable {
+        public volatile boolean done = false;
+        public void run() {
+            try {
+                delay(LONG_DELAY_MS);
+                done = true;
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public static class TrackedNoOpRunnable implements Runnable {
+        public volatile boolean done = false;
+        public void run() {
+            done = true;
+        }
+    }
+
+    public static class TrackedCallable implements Callable {
+        public volatile boolean done = false;
+        public Object call() {
+            try {
+                delay(SMALL_DELAY_MS);
+                done = true;
+            } catch (InterruptedException ok) {}
+            return Boolean.TRUE;
+        }
+    }
+
+    /**
+     * Analog of CheckedRunnable for RecursiveAction
+     */
+    public abstract class CheckedRecursiveAction extends RecursiveAction {
+        protected abstract void realCompute() throws Throwable;
+
+        @Override protected final void compute() {
+            try {
+                realCompute();
+            } catch (Throwable t) {
+                threadUnexpectedException(t);
+            }
+        }
+    }
+
+    /**
+     * Analog of CheckedCallable for RecursiveTask
+     */
+    public abstract class CheckedRecursiveTask<T> extends RecursiveTask<T> {
+        protected abstract T realCompute() throws Throwable;
+
+        @Override protected final T compute() {
+            try {
+                return realCompute();
+            } catch (Throwable t) {
+                threadUnexpectedException(t);
+                return null;
+            }
+        }
+    }
+
+    /**
+     * For use as RejectedExecutionHandler in constructors
+     */
+    public static class NoOpREHandler implements RejectedExecutionHandler {
+        public void rejectedExecution(Runnable r,
+                                      ThreadPoolExecutor executor) {}
+    }
+
+    /**
+     * A CyclicBarrier that uses timed await and fails with
+     * AssertionFailedErrors instead of throwing checked exceptions.
+     */
+    public class CheckedBarrier extends CyclicBarrier {
+        public CheckedBarrier(int parties) { super(parties); }
+
+        public int await() {
+            try {
+                return super.await(2 * LONG_DELAY_MS, MILLISECONDS);
+            } catch (TimeoutException e) {
+                throw new AssertionFailedError("timed out");
+            } catch (Exception e) {
+                AssertionFailedError afe =
+                    new AssertionFailedError("Unexpected exception: " + e);
+                afe.initCause(e);
+                throw afe;
+            }
+        }
+    }
+
+    void checkEmpty(BlockingQueue q) {
+        try {
+            assertTrue(q.isEmpty());
+            assertEquals(0, q.size());
+            assertNull(q.peek());
+            assertNull(q.poll());
+            assertNull(q.poll(0, MILLISECONDS));
+            assertEquals(q.toString(), "[]");
+            assertTrue(Arrays.equals(q.toArray(), new Object[0]));
+            assertFalse(q.iterator().hasNext());
+            try {
+                q.element();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                q.iterator().next();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                q.remove();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+        } catch (InterruptedException ie) {
+            threadUnexpectedException(ie);
+        }
+    }
+
+    void assertSerialEquals(Object x, Object y) {
+        assertTrue(Arrays.equals(serialBytes(x), serialBytes(y)));
+    }
+
+    void assertNotSerialEquals(Object x, Object y) {
+        assertFalse(Arrays.equals(serialBytes(x), serialBytes(y)));
+    }
+
+    byte[] serialBytes(Object o) {
+        try {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(bos);
+            oos.writeObject(o);
+            oos.flush();
+            oos.close();
+            return bos.toByteArray();
+        } catch (Throwable t) {
+            threadUnexpectedException(t);
+            return new byte[0];
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    <T> T serialClone(T o) {
+        try {
+            ObjectInputStream ois = new ObjectInputStream
+                (new ByteArrayInputStream(serialBytes(o)));
+            T clone = (T) ois.readObject();
+            assertSame(o.getClass(), clone.getClass());
+            return clone;
+        } catch (Throwable t) {
+            threadUnexpectedException(t);
+            return null;
+        }
+    }
+
+    public void assertThrows(Class<? extends Throwable> expectedExceptionClass,
+                             Runnable... throwingActions) {
+        for (Runnable throwingAction : throwingActions) {
+            boolean threw = false;
+            try { throwingAction.run(); }
+            catch (Throwable t) {
+                threw = true;
+                if (!expectedExceptionClass.isInstance(t)) {
+                    AssertionFailedError afe =
+                        new AssertionFailedError
+                        ("Expected " + expectedExceptionClass.getName() +
+                         ", got " + t.getClass().getName());
+                    afe.initCause(t);
+                    threadUnexpectedException(afe);
+                }
+            }
+            if (!threw)
+                shouldThrow(expectedExceptionClass.getName());
+        }
+    }
+}
diff --git a/jsr166-tests/src/test/java/jsr166/LinkedBlockingDequeTest.java b/jsr166-tests/src/test/java/jsr166/LinkedBlockingDequeTest.java
new file mode 100644
index 0000000..638a7ac
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/LinkedBlockingDequeTest.java
@@ -0,0 +1,1769 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingDeque;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class LinkedBlockingDequeTest extends JSR166TestCase {
+
+    public static class Unbounded extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new LinkedBlockingDeque();
+        }
+    }
+
+    public static class Bounded extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new LinkedBlockingDeque(SIZE);
+        }
+    }
+
+    /**
+     * Returns a new deque of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private LinkedBlockingDeque<Integer> populatedDeque(int n) {
+        LinkedBlockingDeque<Integer> q =
+            new LinkedBlockingDeque<Integer>(n);
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; i++)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.removeFirst();
+        q.removeFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i, q.size());
+            q.removeFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * offerFirst(null) throws NullPointerException
+     */
+    public void testOfferFirstNull() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        try {
+            q.offerFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offerLast(null) throws NullPointerException
+     */
+    public void testOfferLastNull() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        try {
+            q.offerLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * OfferFirst succeeds
+     */
+    public void testOfferFirst() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        assertTrue(q.offerFirst(new Integer(0)));
+        assertTrue(q.offerFirst(new Integer(1)));
+    }
+
+    /**
+     * OfferLast succeeds
+     */
+    public void testOfferLast() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        assertTrue(q.offerLast(new Integer(0)));
+        assertTrue(q.offerLast(new Integer(1)));
+    }
+
+    /**
+     * pollFirst succeeds unless empty
+     */
+    public void testPollFirst() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * pollLast succeeds unless empty
+     */
+    public void testPollLast() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = SIZE-1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollLast());
+    }
+
+    /**
+     * peekFirst returns next element, or null if empty
+     */
+    public void testPeekFirst() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peekFirst());
+            assertEquals(i, q.pollFirst());
+            assertTrue(q.peekFirst() == null ||
+                       !q.peekFirst().equals(i));
+        }
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.pollFirst());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * peekLast returns next element, or null if empty
+     */
+    public void testPeekLast() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = SIZE-1; i >= 0; --i) {
+            assertEquals(i, q.peekLast());
+            assertEquals(i, q.pollLast());
+            assertTrue(q.peekLast() == null ||
+                       !q.peekLast().equals(i));
+        }
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * getFirst() returns first element, or throws NSEE if empty
+     */
+    public void testFirstElement() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.getFirst());
+            assertEquals(i, q.pollFirst());
+        }
+        try {
+            q.getFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * getLast() returns last element, or throws NSEE if empty
+     */
+    public void testLastElement() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = SIZE-1; i >= 0; --i) {
+            assertEquals(i, q.getLast());
+            assertEquals(i, q.pollLast());
+        }
+        try {
+            q.getLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirst() removes first element, or throws NSEE if empty
+     */
+    public void testRemoveFirst() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.removeFirst());
+        }
+        try {
+            q.removeFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * removeLast() removes last element, or throws NSEE if empty
+     */
+    public void testRemoveLast() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.removeLast());
+        }
+        try {
+            q.removeLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * removeFirstOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveFirstOccurrence() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+            assertFalse(q.removeFirstOccurrence(new Integer(i+1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * removeLastOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveLastOccurrence() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+            assertFalse(q.removeLastOccurrence(new Integer(i+1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * peekFirst returns element inserted with addFirst
+     */
+    public void testAddFirst() {
+        LinkedBlockingDeque q = populatedDeque(3);
+        q.pollLast();
+        q.addFirst(four);
+        assertSame(four, q.peekFirst());
+    }
+
+    /**
+     * peekLast returns element inserted with addLast
+     */
+    public void testAddLast() {
+        LinkedBlockingDeque q = populatedDeque(3);
+        q.pollLast();
+        q.addLast(four);
+        assertSame(four, q.peekLast());
+    }
+
+    /**
+     * A new deque has the indicated capacity, or Integer.MAX_VALUE if
+     * none given
+     */
+    public void testConstructor1() {
+        assertEquals(SIZE, new LinkedBlockingDeque(SIZE).remainingCapacity());
+        assertEquals(Integer.MAX_VALUE, new LinkedBlockingDeque().remainingCapacity());
+    }
+
+    /**
+     * Constructor throws IllegalArgumentException if capacity argument nonpositive
+     */
+    public void testConstructor2() {
+        try {
+            new LinkedBlockingDeque(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Initializing from null Collection throws NullPointerException
+     */
+    public void testConstructor3() {
+        try {
+            new LinkedBlockingDeque(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NullPointerException
+     */
+    public void testConstructor4() {
+        Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            new LinkedBlockingDeque(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws
+     * NullPointerException
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE-1; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new LinkedBlockingDeque(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Deque contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        LinkedBlockingDeque q = new LinkedBlockingDeque(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * Deque transitions from empty to full when elements added
+     */
+    public void testEmptyFull() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        assertTrue(q.isEmpty());
+        assertEquals("should have room for 2", 2, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertFalse(q.offer(three));
+    }
+
+    /**
+     * remainingCapacity decreases on add, increases on remove
+     */
+    public void testRemainingCapacity() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remainingCapacity());
+            assertEquals(SIZE-i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i, q.remainingCapacity());
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * push(null) throws NPE
+     */
+    public void testPushNull() {
+        try {
+            LinkedBlockingDeque q = new LinkedBlockingDeque(1);
+            q.push(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * push succeeds if not full; throws ISE if full
+     */
+    public void testPush() {
+        try {
+            LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+            for (int i = 0; i < SIZE; ++i) {
+                Integer I = new Integer(i);
+                q.push(I);
+                assertEquals(I, q.peek());
+            }
+            assertEquals(0, q.remainingCapacity());
+            q.push(new Integer(SIZE));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * peekFirst returns element inserted with push
+     */
+    public void testPushWithPeek() {
+        LinkedBlockingDeque q = populatedDeque(3);
+        q.pollLast();
+        q.push(four);
+        assertSame(four, q.peekFirst());
+    }
+
+    /**
+     * pop removes next element, or throws NSEE if empty
+     */
+    public void testPop() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pop());
+        }
+        try {
+            q.pop();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * Offer succeeds if not full; fails if full
+     */
+    public void testOffer() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(1);
+        assertTrue(q.offer(zero));
+        assertFalse(q.offer(one));
+    }
+
+    /**
+     * add succeeds if not full; throws ISE if full
+     */
+    public void testAdd() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertTrue(q.add(new Integer(i)));
+        assertEquals(0, q.remainingCapacity());
+        try {
+            q.add(new Integer(SIZE));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE-1; ++i)
+            ints[i] = new Integer(i);
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            q.addAll(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll throws IllegalStateException if not enough room
+     */
+    public void testAddAll4() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE - 1);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            q.addAll(elements);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * Deque contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() throws InterruptedException {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer I = new Integer(i);
+            q.put(I);
+            assertTrue(q.contains(I));
+        }
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly if full
+     */
+    public void testBlockingPut() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i)
+                    q.put(i);
+                assertEquals(SIZE, q.size());
+                assertEquals(0, q.remainingCapacity());
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(SIZE, q.size());
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly waiting for take when full
+     */
+    public void testPutWithTake() throws InterruptedException {
+        final int capacity = 2;
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(capacity);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < capacity; i++)
+                    q.put(i);
+                pleaseTake.countDown();
+                q.put(86);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(0, q.take());
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offer times out if full and elements not taken
+     */
+    public void testTimedOffer() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Object());
+                q.put(new Object());
+                long startTime = System.nanoTime();
+                assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in FIFO order
+     */
+    public void testTake() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.take());
+        }
+    }
+
+    /**
+     * take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final LinkedBlockingDeque q = populatedDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.take());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(i, q.poll(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedDeque(SIZE);
+        final CountDownLatch aboutToWait = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    long t0 = System.nanoTime();
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+                }
+                long t0 = System.nanoTime();
+                aboutToWait.countDown();
+                try {
+                    q.poll(MEDIUM_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {
+                    assertTrue(millisElapsedSince(t0) < MEDIUM_DELAY_MS);
+                }
+            }});
+
+        aboutToWait.await();
+        waitForThreadToEnterWaitState(t, SMALL_DELAY_MS);
+        t.interrupt();
+        awaitTermination(t, MEDIUM_DELAY_MS);
+        checkEmpty(q);
+    }
+
+    /**
+     * putFirst(null) throws NPE
+     */
+    public void testPutFirstNull() throws InterruptedException {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        try {
+            q.putFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * all elements successfully putFirst are contained
+     */
+    public void testPutFirst() throws InterruptedException {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer I = new Integer(i);
+            q.putFirst(I);
+            assertTrue(q.contains(I));
+        }
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * putFirst blocks interruptibly if full
+     */
+    public void testBlockingPutFirst() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i)
+                    q.putFirst(i);
+                assertEquals(SIZE, q.size());
+                assertEquals(0, q.remainingCapacity());
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.putFirst(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.putFirst(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(SIZE, q.size());
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * putFirst blocks interruptibly waiting for take when full
+     */
+    public void testPutFirstWithTake() throws InterruptedException {
+        final int capacity = 2;
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(capacity);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < capacity; i++)
+                    q.putFirst(i);
+                pleaseTake.countDown();
+                q.putFirst(86);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.putFirst(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(capacity - 1, q.take());
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offerFirst times out if full and elements not taken
+     */
+    public void testTimedOfferFirst() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.putFirst(new Object());
+                q.putFirst(new Object());
+                long startTime = System.nanoTime();
+                assertFalse(q.offerFirst(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offerFirst(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in FIFO order
+     */
+    public void testTakeFirst() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.takeFirst());
+        }
+    }
+
+    /**
+     * takeFirst() blocks interruptibly when empty
+     */
+    public void testTakeFirstFromEmptyBlocksInterruptibly() {
+        final BlockingDeque q = new LinkedBlockingDeque();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                threadStarted.countDown();
+                try {
+                    q.takeFirst();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(threadStarted);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * takeFirst() throws InterruptedException immediately if interrupted
+     * before waiting
+     */
+    public void testTakeFirstFromEmptyAfterInterrupt() {
+        final BlockingDeque q = new LinkedBlockingDeque();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                try {
+                    q.takeFirst();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * takeLast() blocks interruptibly when empty
+     */
+    public void testTakeLastFromEmptyBlocksInterruptibly() {
+        final BlockingDeque q = new LinkedBlockingDeque();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                threadStarted.countDown();
+                try {
+                    q.takeLast();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(threadStarted);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * takeLast() throws InterruptedException immediately if interrupted
+     * before waiting
+     */
+    public void testTakeLastFromEmptyAfterInterrupt() {
+        final BlockingDeque q = new LinkedBlockingDeque();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                try {
+                    q.takeLast();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * takeFirst removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTakeFirst() throws InterruptedException {
+        final LinkedBlockingDeque q = populatedDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.takeFirst());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.takeFirst();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.takeFirst();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed pollFirst with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPollFirst0() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst(0, MILLISECONDS));
+        }
+        assertNull(q.pollFirst(0, MILLISECONDS));
+    }
+
+    /**
+     * timed pollFirst with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPollFirst() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(i, q.pollFirst(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.pollFirst(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed pollFirst throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPollFirst() throws InterruptedException {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                LinkedBlockingDeque q = populatedDeque(SIZE);
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.pollFirst(LONG_DELAY_MS, MILLISECONDS));
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.pollFirst(SMALL_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.pollFirst(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed pollFirst before a delayed offerFirst fails; after offerFirst succeeds;
+     * on interruption throws
+     */
+    public void testTimedPollFirstWithOfferFirst() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CheckedBarrier barrier = new CheckedBarrier(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertNull(q.pollFirst(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+
+                barrier.await();
+
+                assertSame(zero, q.pollFirst(LONG_DELAY_MS, MILLISECONDS));
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.pollFirst(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+
+                barrier.await();
+                try {
+                    q.pollFirst(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        barrier.await();
+        long startTime = System.nanoTime();
+        assertTrue(q.offerFirst(zero, LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        barrier.await();
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * putLast(null) throws NPE
+     */
+    public void testPutLastNull() throws InterruptedException {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        try {
+            q.putLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * all elements successfully putLast are contained
+     */
+    public void testPutLast() throws InterruptedException {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer I = new Integer(i);
+            q.putLast(I);
+            assertTrue(q.contains(I));
+        }
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * putLast blocks interruptibly if full
+     */
+    public void testBlockingPutLast() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i)
+                    q.putLast(i);
+                assertEquals(SIZE, q.size());
+                assertEquals(0, q.remainingCapacity());
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.putLast(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.putLast(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(SIZE, q.size());
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * putLast blocks interruptibly waiting for take when full
+     */
+    public void testPutLastWithTake() throws InterruptedException {
+        final int capacity = 2;
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(capacity);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < capacity; i++)
+                    q.putLast(i);
+                pleaseTake.countDown();
+                q.putLast(86);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.putLast(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(0, q.take());
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offerLast times out if full and elements not taken
+     */
+    public void testTimedOfferLast() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.putLast(new Object());
+                q.putLast(new Object());
+                long startTime = System.nanoTime();
+                assertFalse(q.offerLast(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offerLast(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * takeLast retrieves elements in FIFO order
+     */
+    public void testTakeLast() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i-1, q.takeLast());
+        }
+    }
+
+    /**
+     * takeLast removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTakeLast() throws InterruptedException {
+        final LinkedBlockingDeque q = populatedDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(SIZE-i-1, q.takeLast());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.takeLast();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.takeLast();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed pollLast with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPollLast0() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i-1, q.pollLast(0, MILLISECONDS));
+        }
+        assertNull(q.pollLast(0, MILLISECONDS));
+    }
+
+    /**
+     * timed pollLast with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPollLast() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(SIZE-i-1, q.pollLast(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.pollLast(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed pollLast throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPollLast() throws InterruptedException {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                LinkedBlockingDeque q = populatedDeque(SIZE);
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(SIZE-i-1, q.pollLast(LONG_DELAY_MS, MILLISECONDS));
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.pollLast(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.pollLast(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed poll before a delayed offerLast fails; after offerLast succeeds;
+     * on interruption throws
+     */
+    public void testTimedPollWithOfferLast() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CheckedBarrier barrier = new CheckedBarrier(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+
+                barrier.await();
+
+                assertSame(zero, q.poll(LONG_DELAY_MS, MILLISECONDS));
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                barrier.await();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        barrier.await();
+        long startTime = System.nanoTime();
+        assertTrue(q.offerLast(zero, LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+
+        barrier.await();
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            q.poll();
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertEquals(SIZE, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(one));
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        LinkedBlockingDeque p = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        LinkedBlockingDeque p = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE-i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            LinkedBlockingDeque q = populatedDeque(SIZE);
+            LinkedBlockingDeque p = populatedDeque(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE-i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer I = (Integer)(p.remove());
+                assertFalse(q.contains(I));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements in FIFO order
+     */
+    public void testToArray() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        LinkedBlockingDeque<Integer> q = populatedDeque(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.remove());
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertEquals(it.next(), q.take());
+        }
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(3);
+        q.add(two);
+        q.add(one);
+        q.add(three);
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertSame(it.next(), one);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        assertEquals(0, q.remainingCapacity());
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+        assertEquals(0, q.size());
+    }
+
+    /**
+     * Descending iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        int i = 0;
+        Iterator it = q.descendingIterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+        assertFalse(it.hasNext());
+        try {
+            it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * Descending iterator ordering is reverse FIFO
+     */
+    public void testDescendingIteratorOrdering() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque();
+        for (int iters = 0; iters < 100; ++iters) {
+            q.add(new Integer(3));
+            q.add(new Integer(2));
+            q.add(new Integer(1));
+            int k = 0;
+            for (Iterator it = q.descendingIterator(); it.hasNext();) {
+                assertEquals(++k, it.next());
+            }
+
+            assertEquals(3, k);
+            q.remove();
+            q.remove();
+            q.remove();
+        }
+    }
+
+    /**
+     * descendingIterator.remove removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque();
+        for (int iters = 0; iters < 100; ++iters) {
+            q.add(new Integer(3));
+            q.add(new Integer(2));
+            q.add(new Integer(1));
+            Iterator it = q.descendingIterator();
+            assertEquals(it.next(), new Integer(1));
+            it.remove();
+            assertEquals(it.next(), new Integer(2));
+            it = q.descendingIterator();
+            assertEquals(it.next(), new Integer(2));
+            assertEquals(it.next(), new Integer(3));
+            it.remove();
+            assertFalse(it.hasNext());
+            q.remove();
+        }
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * offer transfers elements across Executor tasks
+     */
+    public void testOfferInExecutor() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        q.add(one);
+        q.add(two);
+        ExecutorService executor = Executors.newFixedThreadPool(2);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(q.offer(three));
+                threadsStarted.await();
+                assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(0, q.remainingCapacity());
+            }});
+
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadsStarted.await();
+                assertSame(one, q.take());
+            }});
+
+        joinPool(executor);
+    }
+
+    /**
+     * timed poll retrieves elements across Executor threads
+     */
+    public void testPollInExecutor() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        ExecutorService executor = Executors.newFixedThreadPool(2);
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertNull(q.poll());
+                threadsStarted.await();
+                assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                checkEmpty(q);
+            }});
+
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadsStarted.await();
+                q.put(one);
+            }});
+
+        joinPool(executor);
+    }
+
+    /**
+     * A deserialized serialized deque has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedDeque(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * drainTo(c) empties deque into another collection c
+     */
+    public void testDrainTo() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(SIZE, l.size());
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        q.add(zero);
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(zero));
+        assertTrue(q.contains(one));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i)
+            assertEquals(l.get(i), new Integer(i));
+    }
+
+    /**
+     * drainTo empties full deque, unblocking a waiting put.
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final LinkedBlockingDeque q = populatedDeque(SIZE);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Integer(SIZE+1));
+            }});
+
+        t.start();
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        t.join();
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        for (int i = 0; i < SIZE + 2; ++i) {
+            for (int j = 0; j < SIZE; j++)
+                assertTrue(q.offer(new Integer(j)));
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(k, l.size());
+            assertEquals(SIZE-k, q.size());
+            for (int j = 0; j < k; ++j)
+                assertEquals(l.get(j), new Integer(j));
+            while (q.poll() != null) ;
+        }
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/LinkedBlockingQueueTest.java b/jsr166-tests/src/test/java/jsr166/LinkedBlockingQueueTest.java
new file mode 100644
index 0000000..5628040
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/LinkedBlockingQueueTest.java
@@ -0,0 +1,824 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class LinkedBlockingQueueTest extends JSR166TestCase {
+
+    public static class Unbounded extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new LinkedBlockingQueue();
+        }
+    }
+
+    public static class Bounded extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new LinkedBlockingQueue(SIZE);
+        }
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private LinkedBlockingQueue<Integer> populatedQueue(int n) {
+        LinkedBlockingQueue<Integer> q =
+            new LinkedBlockingQueue<Integer>(n);
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; i++)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * A new queue has the indicated capacity, or Integer.MAX_VALUE if
+     * none given
+     */
+    public void testConstructor1() {
+        assertEquals(SIZE, new LinkedBlockingQueue(SIZE).remainingCapacity());
+        assertEquals(Integer.MAX_VALUE, new LinkedBlockingQueue().remainingCapacity());
+    }
+
+    /**
+     * Constructor throws IllegalArgumentException if capacity argument nonpositive
+     */
+    public void testConstructor2() {
+        try {
+            new LinkedBlockingQueue(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Initializing from null Collection throws NullPointerException
+     */
+    public void testConstructor3() {
+        try {
+            new LinkedBlockingQueue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NullPointerException
+     */
+    public void testConstructor4() {
+        Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            new LinkedBlockingQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws
+     * NullPointerException
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE-1; ++i)
+            ints[i] = new Integer(i);
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new LinkedBlockingQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        LinkedBlockingQueue q = new LinkedBlockingQueue(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * Queue transitions from empty to full when elements added
+     */
+    public void testEmptyFull() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(2);
+        assertTrue(q.isEmpty());
+        assertEquals("should have room for 2", 2, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertFalse(q.offer(three));
+    }
+
+    /**
+     * remainingCapacity decreases on add, increases on remove
+     */
+    public void testRemainingCapacity() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remainingCapacity());
+            assertEquals(SIZE-i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i, q.remainingCapacity());
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * Offer succeeds if not full; fails if full
+     */
+    public void testOffer() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(1);
+        assertTrue(q.offer(zero));
+        assertFalse(q.offer(one));
+    }
+
+    /**
+     * add succeeds if not full; throws IllegalStateException if full
+     */
+    public void testAdd() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertTrue(q.add(new Integer(i)));
+        assertEquals(0, q.remainingCapacity());
+        try {
+            q.add(new Integer(SIZE));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * addAll(this) throws IllegalArgumentException
+     */
+    public void testAddAllSelf() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE-1; ++i)
+            ints[i] = new Integer(i);
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            q.addAll(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll throws IllegalStateException if not enough room
+     */
+    public void testAddAll4() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE - 1);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            q.addAll(elements);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * Queue contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() throws InterruptedException {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer I = new Integer(i);
+            q.put(I);
+            assertTrue(q.contains(I));
+        }
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly if full
+     */
+    public void testBlockingPut() throws InterruptedException {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i)
+                    q.put(i);
+                assertEquals(SIZE, q.size());
+                assertEquals(0, q.remainingCapacity());
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(SIZE, q.size());
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly waiting for take when full
+     */
+    public void testPutWithTake() throws InterruptedException {
+        final int capacity = 2;
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(2);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < capacity; i++)
+                    q.put(i);
+                pleaseTake.countDown();
+                q.put(86);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(0, q.take());
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offer times out if full and elements not taken
+     */
+    public void testTimedOffer() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(2);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Object());
+                q.put(new Object());
+                long startTime = System.nanoTime();
+                assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in FIFO order
+     */
+    public void testTake() throws InterruptedException {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.take());
+        }
+    }
+
+    /**
+     * Take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final BlockingQueue q = populatedQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.take());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        LinkedBlockingQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedQueue(SIZE);
+        final CountDownLatch aboutToWait = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    long t0 = System.nanoTime();
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+                }
+                long t0 = System.nanoTime();
+                aboutToWait.countDown();
+                try {
+                    q.poll(MEDIUM_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {
+                    assertTrue(millisElapsedSince(t0) < MEDIUM_DELAY_MS);
+                }
+            }});
+
+        aboutToWait.await();
+        waitForThreadToEnterWaitState(t, SMALL_DELAY_MS);
+        t.interrupt();
+        awaitTermination(t, MEDIUM_DELAY_MS);
+        checkEmpty(q);
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * An add following remove(x) succeeds
+     */
+    public void testRemoveElementAndAdd() throws InterruptedException {
+        LinkedBlockingQueue q = new LinkedBlockingQueue();
+        assertTrue(q.add(new Integer(1)));
+        assertTrue(q.add(new Integer(2)));
+        assertTrue(q.remove(new Integer(1)));
+        assertTrue(q.remove(new Integer(2)));
+        assertTrue(q.add(new Integer(3)));
+        assertNotNull(q.take());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertEquals(SIZE, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(one));
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        LinkedBlockingQueue p = new LinkedBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        LinkedBlockingQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE-i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            LinkedBlockingQueue q = populatedQueue(SIZE);
+            LinkedBlockingQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE-i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer I = (Integer)(p.remove());
+                assertFalse(q.contains(I));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements in FIFO order
+     */
+    public void testToArray() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() throws InterruptedException {
+        LinkedBlockingQueue<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.poll());
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() throws InterruptedException {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertEquals(it.next(), q.take());
+        }
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(3);
+        q.add(two);
+        q.add(one);
+        q.add(three);
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertSame(it.next(), one);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        assertEquals(0, q.remainingCapacity());
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+        assertEquals(0, q.size());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * offer transfers elements across Executor tasks
+     */
+    public void testOfferInExecutor() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(2);
+        q.add(one);
+        q.add(two);
+        ExecutorService executor = Executors.newFixedThreadPool(2);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(q.offer(three));
+                threadsStarted.await();
+                assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(0, q.remainingCapacity());
+            }});
+
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadsStarted.await();
+                assertSame(one, q.take());
+            }});
+
+        joinPool(executor);
+    }
+
+    /**
+     * timed poll retrieves elements across Executor threads
+     */
+    public void testPollInExecutor() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(2);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        ExecutorService executor = Executors.newFixedThreadPool(2);
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertNull(q.poll());
+                threadsStarted.await();
+                assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                checkEmpty(q);
+            }});
+
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadsStarted.await();
+                q.put(one);
+            }});
+
+        joinPool(executor);
+    }
+
+    /**
+     * A deserialized serialized queue has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * drainTo(c) empties queue into another collection c
+     */
+    public void testDrainTo() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(SIZE, l.size());
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        q.add(zero);
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(zero));
+        assertTrue(q.contains(one));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i)
+            assertEquals(l.get(i), new Integer(i));
+    }
+
+    /**
+     * drainTo empties full queue, unblocking a waiting put.
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final LinkedBlockingQueue q = populatedQueue(SIZE);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Integer(SIZE+1));
+            }});
+
+        t.start();
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        t.join();
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue();
+        for (int i = 0; i < SIZE + 2; ++i) {
+            for (int j = 0; j < SIZE; j++)
+                assertTrue(q.offer(new Integer(j)));
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(k, l.size());
+            assertEquals(SIZE-k, q.size());
+            for (int j = 0; j < k; ++j)
+                assertEquals(l.get(j), new Integer(j));
+            while (q.poll() != null) ;
+        }
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/LinkedListTest.java b/jsr166-tests/src/test/java/jsr166/LinkedListTest.java
new file mode 100644
index 0000000..5b09100
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/LinkedListTest.java
@@ -0,0 +1,627 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+
+public class LinkedListTest extends JSR166TestCase {
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private LinkedList<Integer> populatedQueue(int n) {
+        LinkedList<Integer> q = new LinkedList<Integer>();
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; ++i)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * new queue is empty
+     */
+    public void testConstructor1() {
+        assertEquals(0, new LinkedList().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            LinkedList q = new LinkedList((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        LinkedList q = new LinkedList(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        LinkedList q = new LinkedList();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * offer(null) succeeds
+     */
+    public void testOfferNull() {
+        LinkedList q = new LinkedList();
+        q.offer(null);
+    }
+
+    /**
+     * Offer succeeds
+     */
+    public void testOffer() {
+        LinkedList q = new LinkedList();
+        assertTrue(q.offer(new Integer(0)));
+        assertTrue(q.offer(new Integer(1)));
+    }
+
+    /**
+     * add succeeds
+     */
+    public void testAdd() {
+        LinkedList q = new LinkedList();
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            assertTrue(q.add(new Integer(i)));
+        }
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        try {
+            LinkedList q = new LinkedList();
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        LinkedList q = new LinkedList();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * addAll with too large an index throws IOOBE
+     */
+    public void testAddAll2_IndexOutOfBoundsException() {
+        LinkedList l = new LinkedList();
+        l.add(new Object());
+        LinkedList m = new LinkedList();
+        m.add(new Object());
+        try {
+            l.addAll(4,m);
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * addAll with negative index throws IOOBE
+     */
+    public void testAddAll4_BadIndex() {
+        LinkedList l = new LinkedList();
+        l.add(new Object());
+        LinkedList m = new LinkedList();
+        m.add(new Object());
+        try {
+            l.addAll(-1,m);
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove((Integer)i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i-1));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove((Integer)i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove((Integer)(i+1)));
+            assertFalse(q.contains(i+1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        LinkedList q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertTrue(q.add(new Integer(1)));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        LinkedList q = populatedQueue(SIZE);
+        LinkedList p = new LinkedList();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            assertTrue(p.add(new Integer(i)));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        LinkedList q = populatedQueue(SIZE);
+        LinkedList p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE-i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            LinkedList q = populatedQueue(SIZE);
+            LinkedList p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE-i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer I = (Integer)(p.remove());
+                assertFalse(q.contains(I));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements in FIFO order
+     */
+    public void testToArray() {
+        LinkedList q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        LinkedList<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.poll());
+    }
+
+    /**
+     * toArray(null) throws NullPointerException
+     */
+    public void testToArray_NullArg() {
+        LinkedList l = new LinkedList();
+        l.add(new Object());
+        try {
+            l.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        LinkedList l = new LinkedList();
+        l.add(new Integer(5));
+        try {
+            l.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        LinkedList q = populatedQueue(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final LinkedList q = new LinkedList();
+        q.add(new Integer(1));
+        q.add(new Integer(2));
+        q.add(new Integer(3));
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+
+        assertEquals(3, k);
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final LinkedList q = new LinkedList();
+        q.add(new Integer(1));
+        q.add(new Integer(2));
+        q.add(new Integer(3));
+        Iterator it = q.iterator();
+        assertEquals(1, it.next());
+        it.remove();
+        it = q.iterator();
+        assertEquals(2, it.next());
+        assertEquals(3, it.next());
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * Descending iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        LinkedList q = populatedQueue(SIZE);
+        int i = 0;
+        Iterator it = q.descendingIterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+        assertFalse(it.hasNext());
+        try {
+            it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * Descending iterator ordering is reverse FIFO
+     */
+    public void testDescendingIteratorOrdering() {
+        final LinkedList q = new LinkedList();
+        q.add(new Integer(3));
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        int k = 0;
+        for (Iterator it = q.descendingIterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+
+        assertEquals(3, k);
+    }
+
+    /**
+     * descendingIterator.remove removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final LinkedList q = new LinkedList();
+        q.add(three);
+        q.add(two);
+        q.add(one);
+        Iterator it = q.descendingIterator();
+        it.next();
+        it.remove();
+        it = q.descendingIterator();
+        assertSame(it.next(), two);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        LinkedList q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * peek returns element inserted with addFirst
+     */
+    public void testAddFirst() {
+        LinkedList q = populatedQueue(3);
+        q.addFirst(four);
+        assertSame(four, q.peek());
+    }
+
+    /**
+     * peekFirst returns element inserted with push
+     */
+    public void testPush() {
+        LinkedList q = populatedQueue(3);
+        q.push(four);
+        assertSame(four, q.peekFirst());
+    }
+
+    /**
+     * pop removes next element, or throws NSEE if empty
+     */
+    public void testPop() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pop());
+        }
+        try {
+            q.pop();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * OfferFirst succeeds
+     */
+    public void testOfferFirst() {
+        LinkedList q = new LinkedList();
+        assertTrue(q.offerFirst(new Integer(0)));
+        assertTrue(q.offerFirst(new Integer(1)));
+    }
+
+    /**
+     * OfferLast succeeds
+     */
+    public void testOfferLast() {
+        LinkedList q = new LinkedList();
+        assertTrue(q.offerLast(new Integer(0)));
+        assertTrue(q.offerLast(new Integer(1)));
+    }
+
+    /**
+     * pollLast succeeds unless empty
+     */
+    public void testPollLast() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = SIZE-1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollLast());
+    }
+
+    /**
+     * peekFirst returns next element, or null if empty
+     */
+    public void testPeekFirst() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peekFirst());
+            assertEquals(i, q.pollFirst());
+            assertTrue(q.peekFirst() == null ||
+                       !q.peekFirst().equals(i));
+        }
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * peekLast returns next element, or null if empty
+     */
+    public void testPeekLast() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = SIZE-1; i >= 0; --i) {
+            assertEquals(i, q.peekLast());
+            assertEquals(i, q.pollLast());
+            assertTrue(q.peekLast() == null ||
+                       !q.peekLast().equals(i));
+        }
+        assertNull(q.peekLast());
+    }
+
+    public void testFirstElement() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.getFirst());
+            assertEquals(i, q.pollFirst());
+        }
+        try {
+            q.getFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * getLast returns next element, or throws NSEE if empty
+     */
+    public void testLastElement() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = SIZE-1; i >= 0; --i) {
+            assertEquals(i, q.getLast());
+            assertEquals(i, q.pollLast());
+        }
+        try {
+            q.getLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirstOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveFirstOccurrence() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+            assertFalse(q.removeFirstOccurrence(new Integer(i+1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * removeLastOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveLastOccurrence() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+            assertFalse(q.removeLastOccurrence(new Integer(i+1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java b/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java
new file mode 100644
index 0000000..a14f303
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java
@@ -0,0 +1,1009 @@
+/*
+ * 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 John Vint
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedTransferQueue;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class LinkedTransferQueueTest extends JSR166TestCase {
+
+    public static class Generic extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new LinkedTransferQueue();
+        }
+    }
+
+    /**
+     * Constructor builds new queue with size being zero and empty
+     * being true
+     */
+    public void testConstructor1() {
+        assertEquals(0, new LinkedTransferQueue().size());
+        assertTrue(new LinkedTransferQueue().isEmpty());
+    }
+
+    /**
+     * Initializing constructor with null collection throws
+     * NullPointerException
+     */
+    public void testConstructor2() {
+        try {
+            new LinkedTransferQueue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws
+     * NullPointerException
+     */
+    public void testConstructor3() {
+        Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            new LinkedTransferQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing constructor with a collection containing some null elements
+     * throws NullPointerException
+     */
+    public void testConstructor4() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE-1; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new LinkedTransferQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of the collection it is initialized by
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i) {
+            ints[i] = i;
+        }
+        List intList = Arrays.asList(ints);
+        LinkedTransferQueue q
+            = new LinkedTransferQueue(intList);
+        assertEquals(q.size(), intList.size());
+        assertEquals(q.toString(), intList.toString());
+        assertTrue(Arrays.equals(q.toArray(),
+                                     intList.toArray()));
+        assertTrue(Arrays.equals(q.toArray(new Object[0]),
+                                 intList.toArray(new Object[0])));
+        assertTrue(Arrays.equals(q.toArray(new Object[SIZE]),
+                                 intList.toArray(new Object[SIZE])));
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(ints[i], q.poll());
+        }
+    }
+
+    /**
+     * remainingCapacity() always returns Integer.MAX_VALUE
+     */
+    public void testRemainingCapacity() {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+            assertEquals(SIZE - i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+            assertEquals(i, q.size());
+            q.add(i);
+        }
+    }
+
+    /**
+     * addAll(this) throws IllegalArgumentException
+     */
+    public void testAddAllSelf() {
+        try {
+            LinkedTransferQueue q = populatedQueue(SIZE);
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws
+     * NullPointerException after possibly adding some elements
+     */
+    public void testAddAll3() {
+        try {
+            LinkedTransferQueue q = new LinkedTransferQueue();
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE - 1; ++i) {
+                ints[i] = i;
+            }
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i) {
+            ints[i] = i;
+        }
+        LinkedTransferQueue q = new LinkedTransferQueue();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(ints[i], q.poll());
+        }
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() {
+        LinkedTransferQueue<Integer> q = new LinkedTransferQueue<Integer>();
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.put(i);
+            assertTrue(q.contains(i));
+        }
+    }
+
+    /**
+     * take retrieves elements in FIFO order
+     */
+    public void testTake() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.take());
+        }
+    }
+
+    /**
+     * take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final BlockingQueue q = populatedQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.take());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.poll());
+        }
+        assertNull(q.poll());
+        checkEmpty(q);
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+        checkEmpty(q);
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedQueue(SIZE);
+        final CountDownLatch aboutToWait = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    long t0 = System.nanoTime();
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+                }
+                long t0 = System.nanoTime();
+                aboutToWait.countDown();
+                try {
+                    q.poll(MEDIUM_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {
+                    assertTrue(millisElapsedSince(t0) < MEDIUM_DELAY_MS);
+                }
+            }});
+
+        aboutToWait.await();
+        waitForThreadToEnterWaitState(t, SMALL_DELAY_MS);
+        t.interrupt();
+        awaitTermination(t, MEDIUM_DELAY_MS);
+        checkEmpty(q);
+    }
+
+    /**
+     * timed poll after thread interrupted throws InterruptedException
+     * instead of returning timeout status
+     */
+    public void testTimedPollAfterInterrupt() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedQueue(SIZE);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Thread.currentThread().interrupt();
+                for (int i = 0; i < SIZE; ++i) {
+                    long t0 = System.nanoTime();
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+                }
+                try {
+                    q.poll(MEDIUM_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        awaitTermination(t, MEDIUM_DELAY_MS);
+        checkEmpty(q);
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.peek());
+            assertEquals(i, (int) q.poll());
+            assertTrue(q.peek() == null ||
+                       i != (int) q.peek());
+        }
+        assertNull(q.peek());
+        checkEmpty(q);
+    }
+
+    /**
+     * element returns next element, or throws NoSuchElementException if empty
+     */
+    public void testElement() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.element());
+            assertEquals(i, (int) q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        checkEmpty(q);
+    }
+
+    /**
+     * remove removes next element, or throws NoSuchElementException if empty
+     */
+    public void testRemove() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        checkEmpty(q);
+    }
+
+    /**
+     * An add following remove(x) succeeds
+     */
+    public void testRemoveElementAndAdd() throws InterruptedException {
+        LinkedTransferQueue q = new LinkedTransferQueue();
+        assertTrue(q.add(one));
+        assertTrue(q.add(two));
+        assertTrue(q.remove(one));
+        assertTrue(q.remove(two));
+        assertTrue(q.add(three));
+        assertSame(q.take(), three);
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(i));
+            assertEquals(i, (int) q.poll());
+            assertFalse(q.contains(i));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() throws InterruptedException {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        q.clear();
+        checkEmpty(q);
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertEquals(1, q.size());
+        assertTrue(q.contains(one));
+        q.clear();
+        checkEmpty(q);
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        LinkedTransferQueue<Integer> p = new LinkedTransferQueue<Integer>();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(i);
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true
+     * if changed
+     */
+    public void testRetainAll() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        LinkedTransferQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0) {
+                assertFalse(changed);
+            } else {
+                assertTrue(changed);
+            }
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true
+     * if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            LinkedTransferQueue q = populatedQueue(SIZE);
+            LinkedTransferQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                assertFalse(q.contains(p.remove()));
+            }
+        }
+    }
+
+    /**
+     * toArray() contains all elements in FIFO order
+     */
+    public void testToArray() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++) {
+            assertSame(o[i], q.poll());
+        }
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++) {
+            assertSame(ints[i], q.poll());
+        }
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() throws InterruptedException {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        int i = 0;
+        while (it.hasNext()) {
+            assertEquals(it.next(), i++);
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * iterator.remove() removes current element
+     */
+    public void testIteratorRemove() {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        q.add(two);
+        q.add(one);
+        q.add(three);
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertSame(it.next(), one);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final LinkedTransferQueue<Integer> q
+            = new LinkedTransferQueue<Integer>();
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        int k = 0;
+        for (Integer n : q) {
+            assertEquals(++k, (int) n);
+        }
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+        assertEquals(0, q.size());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * offer transfers elements across Executor tasks
+     */
+    public void testOfferInExecutor() {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        ExecutorService executor = Executors.newFixedThreadPool(2);
+
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadsStarted.await();
+                assertTrue(q.offer(one, LONG_DELAY_MS, MILLISECONDS));
+            }});
+
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadsStarted.await();
+                assertSame(one, q.take());
+                checkEmpty(q);
+            }});
+
+        joinPool(executor);
+    }
+
+    /**
+     * timed poll retrieves elements across Executor threads
+     */
+    public void testPollInExecutor() {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        ExecutorService executor = Executors.newFixedThreadPool(2);
+
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertNull(q.poll());
+                threadsStarted.await();
+                assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                checkEmpty(q);
+            }});
+
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadsStarted.await();
+                q.put(one);
+            }});
+
+        joinPool(executor);
+    }
+
+    /**
+     * A deserialized serialized queue has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * drainTo(c) empties queue into another collection c
+     */
+    public void testDrainTo() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(SIZE, l.size());
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, l.get(i));
+        }
+        q.add(zero);
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(zero));
+        assertTrue(q.contains(one));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i) {
+            assertEquals(i, l.get(i));
+        }
+    }
+
+    /**
+     * drainTo(c) empties full queue, unblocking a waiting put.
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final LinkedTransferQueue q = populatedQueue(SIZE);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                q.put(SIZE + 1);
+            }});
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(i, l.get(i));
+        awaitTermination(t, MEDIUM_DELAY_MS);
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        LinkedTransferQueue q = new LinkedTransferQueue();
+        for (int i = 0; i < SIZE + 2; ++i) {
+            for (int j = 0; j < SIZE; j++) {
+                assertTrue(q.offer(j));
+            }
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(k, l.size());
+            assertEquals(SIZE - k, q.size());
+            for (int j = 0; j < k; ++j)
+                assertEquals(j, l.get(j));
+            while (q.poll() != null)
+                ;
+        }
+    }
+
+    /**
+     * timed poll() or take() increments the waiting consumer count;
+     * offer(e) decrements the waiting consumer count
+     */
+    public void testWaitingConsumer() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        assertEquals(0, q.getWaitingConsumerCount());
+        assertFalse(q.hasWaitingConsumer());
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadStarted.countDown();
+                assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(0, q.getWaitingConsumerCount());
+                assertFalse(q.hasWaitingConsumer());
+            }});
+
+        threadStarted.await();
+        waitForThreadToEnterWaitState(t, SMALL_DELAY_MS);
+        assertEquals(1, q.getWaitingConsumerCount());
+        assertTrue(q.hasWaitingConsumer());
+
+        assertTrue(q.offer(one));
+        assertEquals(0, q.getWaitingConsumerCount());
+        assertFalse(q.hasWaitingConsumer());
+
+        awaitTermination(t, MEDIUM_DELAY_MS);
+    }
+
+    /**
+     * transfer(null) throws NullPointerException
+     */
+    public void testTransfer1() throws InterruptedException {
+        try {
+            LinkedTransferQueue q = new LinkedTransferQueue();
+            q.transfer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * transfer waits until a poll occurs. The transfered element
+     * is returned by this associated poll.
+     */
+    public void testTransfer2() throws InterruptedException {
+        final LinkedTransferQueue<Integer> q
+            = new LinkedTransferQueue<Integer>();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadStarted.countDown();
+                q.transfer(five);
+                checkEmpty(q);
+            }});
+
+        threadStarted.await();
+        waitForThreadToEnterWaitState(t, SMALL_DELAY_MS);
+        assertEquals(1, q.size());
+        assertSame(five, q.poll());
+        checkEmpty(q);
+        awaitTermination(t, MEDIUM_DELAY_MS);
+    }
+
+    /**
+     * transfer waits until a poll occurs, and then transfers in fifo order
+     */
+    public void testTransfer3() throws InterruptedException {
+        final LinkedTransferQueue<Integer> q
+            = new LinkedTransferQueue<Integer>();
+
+        Thread first = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.transfer(four);
+                assertTrue(!q.contains(four));
+                assertEquals(1, q.size());
+            }});
+
+        Thread interruptedThread = newStartedThread(
+            new CheckedInterruptedRunnable() {
+                public void realRun() throws InterruptedException {
+                    while (q.isEmpty())
+                        Thread.yield();
+                    q.transfer(five);
+                }});
+
+        while (q.size() < 2)
+            Thread.yield();
+        assertEquals(2, q.size());
+        assertSame(four, q.poll());
+        first.join();
+        assertEquals(1, q.size());
+        interruptedThread.interrupt();
+        interruptedThread.join();
+        checkEmpty(q);
+    }
+
+    /**
+     * transfer waits until a poll occurs, at which point the polling
+     * thread returns the element
+     */
+    public void testTransfer4() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.transfer(four);
+                assertFalse(q.contains(four));
+                assertSame(three, q.poll());
+            }});
+
+        while (q.isEmpty())
+            Thread.yield();
+        assertFalse(q.isEmpty());
+        assertEquals(1, q.size());
+        assertTrue(q.offer(three));
+        assertSame(four, q.poll());
+        awaitTermination(t, MEDIUM_DELAY_MS);
+    }
+
+    /**
+     * transfer waits until a take occurs. The transfered element
+     * is returned by this associated take.
+     */
+    public void testTransfer5() throws InterruptedException {
+        final LinkedTransferQueue<Integer> q
+            = new LinkedTransferQueue<Integer>();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.transfer(four);
+                checkEmpty(q);
+            }});
+
+        while (q.isEmpty())
+            Thread.yield();
+        assertFalse(q.isEmpty());
+        assertEquals(1, q.size());
+        assertSame(four, q.take());
+        checkEmpty(q);
+        awaitTermination(t, MEDIUM_DELAY_MS);
+    }
+
+    /**
+     * tryTransfer(null) throws NullPointerException
+     */
+    public void testTryTransfer1() {
+        try {
+            final LinkedTransferQueue q = new LinkedTransferQueue();
+            q.tryTransfer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * tryTransfer returns false and does not enqueue if there are no
+     * consumers waiting to poll or take.
+     */
+    public void testTryTransfer2() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        assertFalse(q.tryTransfer(new Object()));
+        assertFalse(q.hasWaitingConsumer());
+        checkEmpty(q);
+    }
+
+    /**
+     * If there is a consumer waiting in timed poll, tryTransfer
+     * returns true while successfully transfering object.
+     */
+    public void testTryTransfer3() throws InterruptedException {
+        final Object hotPotato = new Object();
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                while (! q.hasWaitingConsumer())
+                    Thread.yield();
+                assertTrue(q.hasWaitingConsumer());
+                checkEmpty(q);
+                assertTrue(q.tryTransfer(hotPotato));
+            }});
+
+        assertSame(hotPotato, q.poll(MEDIUM_DELAY_MS, MILLISECONDS));
+        checkEmpty(q);
+        awaitTermination(t, MEDIUM_DELAY_MS);
+    }
+
+    /**
+     * If there is a consumer waiting in take, tryTransfer returns
+     * true while successfully transfering object.
+     */
+    public void testTryTransfer4() throws InterruptedException {
+        final Object hotPotato = new Object();
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                while (! q.hasWaitingConsumer())
+                    Thread.yield();
+                assertTrue(q.hasWaitingConsumer());
+                checkEmpty(q);
+                assertTrue(q.tryTransfer(hotPotato));
+            }});
+
+        assertSame(q.take(), hotPotato);
+        checkEmpty(q);
+        awaitTermination(t, MEDIUM_DELAY_MS);
+    }
+
+    /**
+     * tryTransfer blocks interruptibly if no takers
+     */
+    public void testTryTransfer5() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        assertTrue(q.isEmpty());
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Thread.currentThread().interrupt();
+                try {
+                    q.tryTransfer(new Object(), LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.tryTransfer(new Object(), LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * tryTransfer gives up after the timeout and returns false
+     */
+    public void testTryTransfer6() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long t0 = System.nanoTime();
+                assertFalse(q.tryTransfer(new Object(),
+                                          timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(t0) >= timeoutMillis());
+                checkEmpty(q);
+            }});
+
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * tryTransfer waits for any elements previously in to be removed
+     * before transfering to a poll or take
+     */
+    public void testTryTransfer7() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        assertTrue(q.offer(four));
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertTrue(q.tryTransfer(five, MEDIUM_DELAY_MS, MILLISECONDS));
+                checkEmpty(q);
+            }});
+
+        while (q.size() != 2)
+            Thread.yield();
+        assertEquals(2, q.size());
+        assertSame(four, q.poll());
+        assertSame(five, q.poll());
+        checkEmpty(q);
+        awaitTermination(t, MEDIUM_DELAY_MS);
+    }
+
+    /**
+     * tryTransfer attempts to enqueue into the queue and fails
+     * returning false not enqueueing and the successive poll is null
+     */
+    public void testTryTransfer8() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        assertTrue(q.offer(four));
+        assertEquals(1, q.size());
+        long t0 = System.nanoTime();
+        assertFalse(q.tryTransfer(five, timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(t0) >= timeoutMillis());
+        assertEquals(1, q.size());
+        assertSame(four, q.poll());
+        assertNull(q.poll());
+        checkEmpty(q);
+    }
+
+    private LinkedTransferQueue<Integer> populatedQueue(int n) {
+        LinkedTransferQueue<Integer> q = new LinkedTransferQueue<Integer>();
+        checkEmpty(q);
+        for (int i = 0; i < n; i++) {
+            assertEquals(i, q.size());
+            assertTrue(q.offer(i));
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        }
+        assertFalse(q.isEmpty());
+        return q;
+    }
+}
diff --git a/jsr166-tests/src/test/java/jsr166/LockSupportTest.java b/jsr166-tests/src/test/java/jsr166/LockSupportTest.java
new file mode 100644
index 0000000..051de35
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/LockSupportTest.java
@@ -0,0 +1,362 @@
+/*
+ * 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 junit.framework.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.LockSupport;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class LockSupportTest extends JSR166TestCase {
+
+    /**
+     * 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);
+    }
+}
diff --git a/jsr166-tests/src/test/java/jsr166/PhaserTest.java b/jsr166-tests/src/test/java/jsr166/PhaserTest.java
new file mode 100644
index 0000000..3889c1f
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/PhaserTest.java
@@ -0,0 +1,786 @@
+/*
+ * 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 John Vint
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeoutException;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class PhaserTest extends JSR166TestCase {
+
+    private static final int maxParties = 65535;
+
+    /** Checks state of unterminated phaser. */
+    protected void assertState(Phaser phaser,
+                               int phase, int parties, int unarrived) {
+        assertEquals(phase, phaser.getPhase());
+        assertEquals(parties, phaser.getRegisteredParties());
+        assertEquals(unarrived, phaser.getUnarrivedParties());
+        assertEquals(parties - unarrived, phaser.getArrivedParties());
+        assertFalse(phaser.isTerminated());
+    }
+
+    /** Checks state of terminated phaser. */
+    protected void assertTerminated(Phaser phaser, int maxPhase, int parties) {
+        assertTrue(phaser.isTerminated());
+        int expectedPhase = maxPhase + Integer.MIN_VALUE;
+        assertEquals(expectedPhase, phaser.getPhase());
+        assertEquals(parties, phaser.getRegisteredParties());
+        assertEquals(expectedPhase, phaser.register());
+        assertEquals(expectedPhase, phaser.arrive());
+        assertEquals(expectedPhase, phaser.arriveAndDeregister());
+    }
+
+    protected void assertTerminated(Phaser phaser, int maxPhase) {
+        assertTerminated(phaser, maxPhase, 0);
+    }
+
+    /**
+     * Empty constructor builds a new Phaser with no parent, no registered
+     * parties and initial phase number of 0
+     */
+    public void testConstructorDefaultValues() {
+        Phaser phaser = new Phaser();
+        assertNull(phaser.getParent());
+        assertEquals(0, phaser.getRegisteredParties());
+        assertEquals(0, phaser.getArrivedParties());
+        assertEquals(0, phaser.getUnarrivedParties());
+        assertEquals(0, phaser.getPhase());
+    }
+
+    /**
+     * Constructing with a negative number of parties throws
+     * IllegalArgumentException
+     */
+    public void testConstructorNegativeParties() {
+        try {
+            new Phaser(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructing with a negative number of parties throws
+     * IllegalArgumentException
+     */
+    public void testConstructorNegativeParties2() {
+        try {
+            new Phaser(new Phaser(), -1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructing with a number of parties > 65535 throws
+     * IllegalArgumentException
+     */
+    public void testConstructorPartiesExceedsLimit() {
+        new Phaser(maxParties);
+        try {
+            new Phaser(maxParties + 1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+
+        new Phaser(new Phaser(), maxParties);
+        try {
+            new Phaser(new Phaser(), maxParties + 1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * The parent provided to the constructor should be returned from
+     * a later call to getParent
+     */
+    public void testConstructor3() {
+        Phaser parent = new Phaser();
+        assertSame(parent, new Phaser(parent).getParent());
+        assertNull(new Phaser(null).getParent());
+    }
+
+    /**
+     * The parent being input into the parameter should equal the original
+     * parent when being returned
+     */
+    public void testConstructor5() {
+        Phaser parent = new Phaser();
+        assertSame(parent, new Phaser(parent, 0).getParent());
+        assertNull(new Phaser(null, 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();
+        assertState(phaser, 0, 0, 0);
+        assertEquals(0, phaser.register());
+        assertState(phaser, 0, 1, 1);
+    }
+
+    /**
+     * Registering more than 65536 parties causes IllegalStateException
+     */
+    public void testRegister2() {
+        Phaser phaser = new Phaser(0);
+        assertState(phaser, 0, 0, 0);
+        assertEquals(0, phaser.bulkRegister(maxParties - 10));
+        assertState(phaser, 0, maxParties - 10, maxParties - 10);
+        for (int i = 0; i < 10; i++) {
+            assertState(phaser, 0, maxParties - 10 + i, maxParties - 10 + i);
+            assertEquals(0, phaser.register());
+        }
+        assertState(phaser, 0, maxParties, maxParties);
+        try {
+            phaser.register();
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+
+        try {
+            phaser.bulkRegister(Integer.MAX_VALUE);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+
+        assertEquals(0, phaser.bulkRegister(0));
+        assertState(phaser, 0, maxParties, maxParties);
+    }
+
+    /**
+     * register() correctly returns the current barrier phase number
+     * when invoked
+     */
+    public void testRegister3() {
+        Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        assertEquals(0, phaser.arrive());
+        assertEquals(1, phaser.register());
+        assertState(phaser, 1, 2, 2);
+    }
+
+    /**
+     * register causes the next arrive to not increment the phase
+     * rather retain the phase number
+     */
+    public void testRegister4() {
+        Phaser phaser = new Phaser(1);
+        assertEquals(0, phaser.arrive());
+        assertEquals(1, phaser.register());
+        assertEquals(1, phaser.arrive());
+        assertState(phaser, 1, 2, 1);
+    }
+
+    /**
+     * register on a subphaser that is currently empty succeeds, even
+     * in the presence of another non-empty subphaser
+     */
+    public void testRegisterEmptySubPhaser() {
+        Phaser root = new Phaser();
+        Phaser child1 = new Phaser(root, 1);
+        Phaser child2 = new Phaser(root, 0);
+        assertEquals(0, child2.register());
+        assertState(root, 0, 2, 2);
+        assertState(child1, 0, 1, 1);
+        assertState(child2, 0, 1, 1);
+        assertEquals(0, child2.arriveAndDeregister());
+        assertState(root, 0, 1, 1);
+        assertState(child1, 0, 1, 1);
+        assertState(child2, 0, 0, 0);
+        assertEquals(0, child2.register());
+        assertEquals(0, child2.arriveAndDeregister());
+        assertState(root, 0, 1, 1);
+        assertState(child1, 0, 1, 1);
+        assertState(child2, 0, 0, 0);
+        assertEquals(0, child1.arriveAndDeregister());
+        assertTerminated(root, 1);
+        assertTerminated(child1, 1);
+        assertTerminated(child2, 1);
+    }
+
+    /**
+     * 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();
+        assertEquals(0, phaser.bulkRegister(0));
+        assertState(phaser, 0, 0, 0);
+        assertEquals(0, phaser.bulkRegister(20));
+        assertState(phaser, 0, 20, 20);
+    }
+
+    /**
+     * Registering with a number of parties greater than or equal to 1<<16
+     * throws IllegalStateException.
+     */
+    public void testBulkRegister3() {
+        assertEquals(0, new Phaser().bulkRegister((1 << 16) - 1));
+
+        try {
+            new Phaser().bulkRegister(1 << 16);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+
+        try {
+            new Phaser(2).bulkRegister((1 << 16) - 2);
+            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);
+        assertState(phaser, 0, 1, 1);
+        assertEquals(0, phaser.arrive());
+        assertState(phaser, 1, 1, 1);
+    }
+
+    /**
+     * arriveAndDeregister does not wait for others to arrive at barrier
+     */
+    public void testArriveAndDeregister() {
+        final Phaser phaser = new Phaser(1);
+        for (int i = 0; i < 10; i++) {
+            assertState(phaser, 0, 1, 1);
+            assertEquals(0, phaser.register());
+            assertState(phaser, 0, 2, 2);
+            assertEquals(0, phaser.arriveAndDeregister());
+            assertState(phaser, 0, 1, 1);
+        }
+        assertEquals(0, phaser.arriveAndDeregister());
+        assertTerminated(phaser, 1);
+    }
+
+    /**
+     * arriveAndDeregister does not wait for others to arrive at barrier
+     */
+    public void testArrive2() {
+        final Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        List<Thread> threads = new ArrayList<Thread>();
+        for (int i = 0; i < 10; i++) {
+            assertEquals(0, phaser.register());
+            threads.add(newStartedThread(new CheckedRunnable() {
+                public void realRun() {
+                    assertEquals(0, phaser.arriveAndDeregister());
+                }}));
+        }
+
+        for (Thread thread : threads)
+            awaitTermination(thread);
+        assertState(phaser, 0, 1, 1);
+        assertEquals(0, phaser.arrive());
+        assertState(phaser, 1, 1, 1);
+    }
+
+    /**
+     * arrive() returns a negative number if the Phaser is terminated
+     */
+    public void testArrive3() {
+        Phaser phaser = new Phaser(1);
+        phaser.forceTermination();
+        assertTerminated(phaser, 0, 1);
+        assertEquals(0, phaser.getPhase() + Integer.MIN_VALUE);
+        assertTrue(phaser.arrive() < 0);
+        assertTrue(phaser.register() < 0);
+        assertTrue(phaser.arriveAndDeregister() < 0);
+        assertTrue(phaser.awaitAdvance(1) < 0);
+        assertTrue(phaser.getPhase() < 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 reduces the number of arrived parties
+     */
+    public void testArriveAndDeregister2() {
+        final Phaser phaser = new Phaser(1);
+        assertEquals(0, phaser.register());
+        assertEquals(0, phaser.arrive());
+        assertState(phaser, 0, 2, 1);
+        assertEquals(0, phaser.arriveAndDeregister());
+        assertState(phaser, 1, 1, 1);
+    }
+
+    /**
+     * arriveAndDeregister arrives at 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 testArriveAndDeregister3() {
+        Phaser parent = new Phaser();
+        Phaser child = new Phaser(parent);
+        assertState(child, 0, 0, 0);
+        assertState(parent, 0, 0, 0);
+        assertEquals(0, child.register());
+        assertState(child, 0, 1, 1);
+        assertState(parent, 0, 1, 1);
+        assertEquals(0, child.arriveAndDeregister());
+        assertTerminated(child, 1);
+        assertTerminated(parent, 1);
+    }
+
+    /**
+     * arriveAndDeregister deregisters one party from its parent when
+     * the number of parties of child is zero after deregistration
+     */
+    public void testArriveAndDeregister4() {
+        Phaser parent = new Phaser();
+        Phaser child = new Phaser(parent);
+        assertEquals(0, parent.register());
+        assertEquals(0, child.register());
+        assertState(child, 0, 1, 1);
+        assertState(parent, 0, 2, 2);
+        assertEquals(0, child.arriveAndDeregister());
+        assertState(child, 0, 0, 0);
+        assertState(parent, 0, 1, 1);
+    }
+
+    /**
+     * arriveAndDeregister deregisters one party from its parent when
+     * the number of parties of root is nonzero after deregistration.
+     */
+    public void testArriveAndDeregister5() {
+        Phaser root = new Phaser();
+        Phaser parent = new Phaser(root);
+        Phaser child = new Phaser(parent);
+        assertState(root, 0, 0, 0);
+        assertState(parent, 0, 0, 0);
+        assertState(child, 0, 0, 0);
+        assertEquals(0, child.register());
+        assertState(root, 0, 1, 1);
+        assertState(parent, 0, 1, 1);
+        assertState(child, 0, 1, 1);
+        assertEquals(0, child.arriveAndDeregister());
+        assertTerminated(child, 1);
+        assertTerminated(parent, 1);
+        assertTerminated(root, 1);
+    }
+
+    /**
+     * arriveAndDeregister returns the phase in which it leaves the
+     * phaser in after deregistration
+     */
+    public void testArriveAndDeregister6() {
+        final Phaser phaser = new Phaser(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(0, phaser.arrive());
+            }});
+        assertEquals(1, phaser.arriveAndAwaitAdvance());
+        assertState(phaser, 1, 2, 2);
+        assertEquals(1, phaser.arriveAndDeregister());
+        assertState(phaser, 1, 1, 1);
+        assertEquals(1, phaser.arriveAndDeregister());
+        assertTerminated(phaser, 2);
+        awaitTermination(t);
+    }
+
+    /**
+     * awaitAdvance succeeds upon advance
+     */
+    public void testAwaitAdvance1() {
+        final Phaser phaser = new Phaser(1);
+        assertEquals(0, phaser.arrive());
+        assertEquals(1, phaser.awaitAdvance(0));
+    }
+
+    /**
+     * awaitAdvance with a negative parameter will return without affecting the
+     * phaser
+     */
+    public void testAwaitAdvance2() {
+        Phaser phaser = new Phaser();
+        assertTrue(phaser.awaitAdvance(-1) < 0);
+        assertState(phaser, 0, 0, 0);
+    }
+
+    /**
+     * awaitAdvanceInterruptibly blocks interruptibly
+     */
+    public void testAwaitAdvanceInterruptibly_interruptible() throws InterruptedException {
+        final Phaser phaser = new Phaser(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(2);
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                try {
+                    phaser.awaitAdvanceInterruptibly(0);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    phaser.awaitAdvanceInterruptibly(0);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws TimeoutException {
+                Thread.currentThread().interrupt();
+                try {
+                    phaser.awaitAdvanceInterruptibly(0, 2*LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    phaser.awaitAdvanceInterruptibly(0, 2*LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertState(phaser, 0, 1, 1);
+        assertThreadsStayAlive(t1, t2);
+        t1.interrupt();
+        t2.interrupt();
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertState(phaser, 0, 1, 1);
+        assertEquals(0, phaser.arrive());
+        assertState(phaser, 1, 1, 1);
+    }
+
+    /**
+     * awaitAdvance continues waiting if interrupted before waiting
+     */
+    public void testAwaitAdvanceAfterInterrupt() {
+        final Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        final CountDownLatch pleaseArrive = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                assertEquals(0, phaser.register());
+                assertEquals(0, phaser.arrive());
+                pleaseArrive.countDown();
+                assertTrue(Thread.currentThread().isInterrupted());
+                assertEquals(1, phaser.awaitAdvance(0));
+                assertTrue(Thread.interrupted());
+            }});
+
+        await(pleaseArrive);
+        waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+        assertEquals(0, phaser.arrive());
+        awaitTermination(t);
+
+        Thread.currentThread().interrupt();
+        assertEquals(1, phaser.awaitAdvance(0));
+        assertTrue(Thread.interrupted());
+    }
+
+    /**
+     *  awaitAdvance continues waiting if interrupted while waiting
+     */
+    public void testAwaitAdvanceBeforeInterrupt() {
+        final Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        final CountDownLatch pleaseArrive = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(0, phaser.register());
+                assertEquals(0, phaser.arrive());
+                assertFalse(Thread.currentThread().isInterrupted());
+                pleaseArrive.countDown();
+                assertEquals(1, phaser.awaitAdvance(0));
+                assertTrue(Thread.interrupted());
+            }});
+
+        await(pleaseArrive);
+        waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+        t.interrupt();
+        assertEquals(0, phaser.arrive());
+        awaitTermination(t);
+
+        Thread.currentThread().interrupt();
+        assertEquals(1, phaser.awaitAdvance(0));
+        assertTrue(Thread.interrupted());
+    }
+
+    /**
+     * arriveAndAwaitAdvance continues waiting if interrupted before waiting
+     */
+    public void testArriveAndAwaitAdvanceAfterInterrupt() {
+        final Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                assertEquals(0, phaser.register());
+                pleaseInterrupt.countDown();
+                assertTrue(Thread.currentThread().isInterrupted());
+                assertEquals(1, phaser.arriveAndAwaitAdvance());
+                assertTrue(Thread.currentThread().isInterrupted());
+            }});
+
+        await(pleaseInterrupt);
+        waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+        Thread.currentThread().interrupt();
+        assertEquals(1, phaser.arriveAndAwaitAdvance());
+        assertTrue(Thread.interrupted());
+        awaitTermination(t);
+    }
+
+    /**
+     * arriveAndAwaitAdvance continues waiting if interrupted while waiting
+     */
+    public void testArriveAndAwaitAdvanceBeforeInterrupt() {
+        final Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(0, phaser.register());
+                assertFalse(Thread.currentThread().isInterrupted());
+                pleaseInterrupt.countDown();
+                assertEquals(1, phaser.arriveAndAwaitAdvance());
+                assertTrue(Thread.currentThread().isInterrupted());
+            }});
+
+        await(pleaseInterrupt);
+        waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+        t.interrupt();
+        Thread.currentThread().interrupt();
+        assertEquals(1, phaser.arriveAndAwaitAdvance());
+        assertTrue(Thread.interrupted());
+        awaitTermination(t);
+    }
+
+    /**
+     * awaitAdvance atomically waits for all parties within the same phase to
+     * complete before continuing
+     */
+    public void testAwaitAdvance4() {
+        final Phaser phaser = new Phaser(4);
+        final AtomicInteger count = new AtomicInteger(0);
+        List<Thread> threads = new ArrayList<Thread>();
+        for (int i = 0; i < 4; i++)
+            threads.add(newStartedThread(new CheckedRunnable() {
+                public void realRun() {
+                    for (int k = 0; k < 3; k++) {
+                        assertEquals(2*k+1, phaser.arriveAndAwaitAdvance());
+                        count.incrementAndGet();
+                        assertEquals(2*k+1, phaser.arrive());
+                        assertEquals(2*k+2, phaser.awaitAdvance(2*k+1));
+                        assertEquals(4*(k+1), count.get());
+                    }}}));
+
+        for (Thread thread : threads)
+            awaitTermination(thread);
+    }
+
+    /**
+     * awaitAdvance returns the current phase
+     */
+    public void testAwaitAdvance5() {
+        final Phaser phaser = new Phaser(1);
+        assertEquals(1, phaser.awaitAdvance(phaser.arrive()));
+        assertEquals(1, phaser.getPhase());
+        assertEquals(1, phaser.register());
+        List<Thread> threads = new ArrayList<Thread>();
+        for (int i = 0; i < 8; i++) {
+            final CountDownLatch latch = new CountDownLatch(1);
+            final boolean goesFirst = ((i & 1) == 0);
+            threads.add(newStartedThread(new CheckedRunnable() {
+                public void realRun() {
+                    if (goesFirst)
+                        latch.countDown();
+                    else
+                        await(latch);
+                    phaser.arrive();
+                }}));
+            if (goesFirst)
+                await(latch);
+            else
+                latch.countDown();
+            assertEquals(i + 2, phaser.awaitAdvance(phaser.arrive()));
+            assertEquals(i + 2, phaser.getPhase());
+        }
+        for (Thread thread : threads)
+            awaitTermination(thread);
+    }
+
+    /**
+     * awaitAdvance returns the current phase in child phasers
+     */
+    public void testAwaitAdvanceTieredPhaser() throws Exception {
+        final Phaser parent = new Phaser();
+        final List<Phaser> zeroPartyChildren = new ArrayList<Phaser>(3);
+        final List<Phaser> onePartyChildren = new ArrayList<Phaser>(3);
+        for (int i = 0; i < 3; i++) {
+            zeroPartyChildren.add(new Phaser(parent, 0));
+            onePartyChildren.add(new Phaser(parent, 1));
+        }
+        final List<Phaser> phasers = new ArrayList<Phaser>();
+        phasers.addAll(zeroPartyChildren);
+        phasers.addAll(onePartyChildren);
+        phasers.add(parent);
+        for (Phaser phaser : phasers) {
+            assertEquals(-42, phaser.awaitAdvance(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, SMALL_DELAY_MS, MILLISECONDS));
+        }
+
+        for (Phaser child : onePartyChildren)
+            assertEquals(0, child.arrive());
+        for (Phaser phaser : phasers) {
+            assertEquals(-42, phaser.awaitAdvance(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(1, phaser.awaitAdvance(0));
+            assertEquals(1, phaser.awaitAdvanceInterruptibly(0));
+            assertEquals(1, phaser.awaitAdvanceInterruptibly(0, SMALL_DELAY_MS, MILLISECONDS));
+        }
+
+        for (Phaser child : onePartyChildren)
+            assertEquals(1, child.arrive());
+        for (Phaser phaser : phasers) {
+            assertEquals(-42, phaser.awaitAdvance(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(2, phaser.awaitAdvance(0));
+            assertEquals(2, phaser.awaitAdvanceInterruptibly(0));
+            assertEquals(2, phaser.awaitAdvanceInterruptibly(0, SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(2, phaser.awaitAdvance(1));
+            assertEquals(2, phaser.awaitAdvanceInterruptibly(1));
+            assertEquals(2, phaser.awaitAdvanceInterruptibly(1, SMALL_DELAY_MS, MILLISECONDS));
+        }
+    }
+
+    /**
+     * awaitAdvance returns when the phaser is externally terminated
+     */
+    public void testAwaitAdvance6() {
+        final Phaser phaser = new Phaser(3);
+        final CountDownLatch pleaseForceTermination = new CountDownLatch(2);
+        final List<Thread> threads = new ArrayList<Thread>();
+        for (int i = 0; i < 2; i++) {
+            Runnable r = new CheckedRunnable() {
+                public void realRun() {
+                    assertEquals(0, phaser.arrive());
+                    pleaseForceTermination.countDown();
+                    assertTrue(phaser.awaitAdvance(0) < 0);
+                    assertTrue(phaser.isTerminated());
+                    assertTrue(phaser.getPhase() < 0);
+                    assertEquals(0, phaser.getPhase() + Integer.MIN_VALUE);
+                    assertEquals(3, phaser.getRegisteredParties());
+                }};
+            threads.add(newStartedThread(r));
+        }
+        await(pleaseForceTermination);
+        phaser.forceTermination();
+        assertTrue(phaser.isTerminated());
+        assertEquals(0, phaser.getPhase() + Integer.MIN_VALUE);
+        for (Thread thread : threads)
+            awaitTermination(thread);
+        assertEquals(3, phaser.getRegisteredParties());
+    }
+
+    /**
+     * arriveAndAwaitAdvance throws IllegalStateException with no
+     * unarrived parties
+     */
+    public void testArriveAndAwaitAdvance1() {
+        try {
+            Phaser phaser = new Phaser();
+            phaser.arriveAndAwaitAdvance();
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * 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 int THREADS = 3;
+        final CountDownLatch pleaseArrive = new CountDownLatch(THREADS);
+        final List<Thread> threads = new ArrayList<Thread>();
+        for (int i = 0; i < THREADS; i++)
+            threads.add(newStartedThread(new CheckedRunnable() {
+                public void realRun() {
+                    assertEquals(0, phaser.register());
+                    pleaseArrive.countDown();
+                    assertEquals(1, phaser.arriveAndAwaitAdvance());
+                }}));
+
+        await(pleaseArrive);
+        long startTime = System.nanoTime();
+        while (phaser.getArrivedParties() < THREADS)
+            Thread.yield();
+        assertEquals(THREADS, phaser.getArrivedParties());
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        for (Thread thread : threads)
+            waitForThreadToEnterWaitState(thread, SHORT_DELAY_MS);
+        for (Thread thread : threads)
+            assertTrue(thread.isAlive());
+        assertState(phaser, 0, THREADS + 1, 1);
+        phaser.arriveAndAwaitAdvance();
+        for (Thread thread : threads)
+            awaitTermination(thread);
+        assertState(phaser, 1, THREADS + 1, THREADS + 1);
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/PriorityBlockingQueueTest.java b/jsr166-tests/src/test/java/jsr166/PriorityBlockingQueueTest.java
new file mode 100644
index 0000000..908910b
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/PriorityBlockingQueueTest.java
@@ -0,0 +1,711 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.PriorityBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class PriorityBlockingQueueTest extends JSR166TestCase {
+
+    public static class Generic extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new PriorityBlockingQueue();
+        }
+    }
+
+    public static class InitialCapacity extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new PriorityBlockingQueue(SIZE);
+        }
+    }
+
+    private static final int NOCAP = Integer.MAX_VALUE;
+
+    /** Sample Comparator */
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private PriorityBlockingQueue<Integer> populatedQueue(int n) {
+        PriorityBlockingQueue<Integer> q =
+            new PriorityBlockingQueue<Integer>(n);
+        assertTrue(q.isEmpty());
+        for (int i = n-1; i >= 0; i-=2)
+            assertTrue(q.offer(new Integer(i)));
+        for (int i = (n & 1); i < n; i+=2)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(NOCAP, q.remainingCapacity());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * A new queue has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(NOCAP, new PriorityBlockingQueue(SIZE).remainingCapacity());
+    }
+
+    /**
+     * Constructor throws IAE if capacity argument nonpositive
+     */
+    public void testConstructor2() {
+        try {
+            new PriorityBlockingQueue(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new PriorityBlockingQueue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            new PriorityBlockingQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE-1; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new PriorityBlockingQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        PriorityBlockingQueue q = new PriorityBlockingQueue(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * The comparator used in constructor is used
+     */
+    public void testConstructor7() {
+        MyReverseComparator cmp = new MyReverseComparator();
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE, cmp);
+        assertEquals(cmp, q.comparator());
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        q.addAll(Arrays.asList(ints));
+        for (int i = SIZE-1; i >= 0; --i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(2);
+        assertTrue(q.isEmpty());
+        assertEquals(NOCAP, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * remainingCapacity does not change when elements added or removed,
+     * but size does
+     */
+    public void testRemainingCapacity() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(NOCAP, q.remainingCapacity());
+            assertEquals(SIZE-i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(NOCAP, q.remainingCapacity());
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * Offer of comparable element succeeds
+     */
+    public void testOffer() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(1);
+        assertTrue(q.offer(zero));
+        assertTrue(q.offer(one));
+    }
+
+    /**
+     * Offer of non-Comparable throws CCE
+     */
+    public void testOfferNonComparable() {
+        try {
+            PriorityBlockingQueue q = new PriorityBlockingQueue(1);
+            q.offer(new Object());
+            q.offer(new Object());
+            q.offer(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * add of comparable succeeds
+     */
+    public void testAdd() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            assertTrue(q.add(new Integer(i)));
+        }
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        try {
+            PriorityBlockingQueue q = populatedQueue(SIZE);
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        try {
+            PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE);
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i);
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = SIZE-1; i >= 0; --i)
+            ints[i] = new Integer(i);
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE);
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer I = new Integer(i);
+            q.put(I);
+            assertTrue(q.contains(I));
+        }
+        assertEquals(SIZE, q.size());
+    }
+
+    /**
+     * put doesn't block waiting for take
+     */
+    public void testPutWithTake() throws InterruptedException {
+        final PriorityBlockingQueue q = new PriorityBlockingQueue(2);
+        final int size = 4;
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                for (int i = 0; i < size; i++)
+                    q.put(new Integer(0));
+            }});
+
+        awaitTermination(t);
+        assertEquals(size, q.size());
+        q.take();
+    }
+
+    /**
+     * timed offer does not time out
+     */
+    public void testTimedOffer() throws InterruptedException {
+        final PriorityBlockingQueue q = new PriorityBlockingQueue(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                q.put(new Integer(0));
+                q.put(new Integer(0));
+                assertTrue(q.offer(new Integer(0), SHORT_DELAY_MS, MILLISECONDS));
+                assertTrue(q.offer(new Integer(0), LONG_DELAY_MS, MILLISECONDS));
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in priority order
+     */
+    public void testTake() throws InterruptedException {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.take());
+        }
+    }
+
+    /**
+     * Take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final PriorityBlockingQueue q = populatedQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.take());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        PriorityBlockingQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedQueue(SIZE);
+        final CountDownLatch aboutToWait = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    long t0 = System.nanoTime();
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+                }
+                long t0 = System.nanoTime();
+                aboutToWait.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {
+                    assertTrue(millisElapsedSince(t0) < MEDIUM_DELAY_MS);
+                }
+            }});
+
+        aboutToWait.await();
+        waitForThreadToEnterWaitState(t, SMALL_DELAY_MS);
+        t.interrupt();
+        awaitTermination(t, MEDIUM_DELAY_MS);
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(one));
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        PriorityBlockingQueue p = new PriorityBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        PriorityBlockingQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE-i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            PriorityBlockingQueue q = populatedQueue(SIZE);
+            PriorityBlockingQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE-i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer I = (Integer)(p.remove());
+                assertFalse(q.contains(I));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements
+     */
+    public void testToArray() throws InterruptedException {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        Arrays.sort(o);
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.take());
+    }
+
+    /**
+     * toArray(a) contains all elements
+     */
+    public void testToArray2() throws InterruptedException {
+        PriorityBlockingQueue<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        Arrays.sort(ints);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.take());
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final PriorityBlockingQueue q = new PriorityBlockingQueue(3);
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * timed poll transfers elements across Executor tasks
+     */
+    public void testPollInExecutor() {
+        final PriorityBlockingQueue q = new PriorityBlockingQueue(2);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        ExecutorService executor = Executors.newFixedThreadPool(2);
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertNull(q.poll());
+                threadsStarted.await();
+                assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                checkEmpty(q);
+            }});
+
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadsStarted.await();
+                q.put(one);
+            }});
+
+        joinPool(executor);
+    }
+
+    /**
+     * A deserialized serialized queue has same elements
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * drainTo(c) empties queue into another collection c
+     */
+    public void testDrainTo() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(SIZE, l.size());
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        q.add(zero);
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(zero));
+        assertTrue(q.contains(one));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i)
+            assertEquals(l.get(i), new Integer(i));
+    }
+
+    /**
+     * drainTo empties queue
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final PriorityBlockingQueue q = populatedQueue(SIZE);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                q.put(new Integer(SIZE+1));
+            }});
+
+        t.start();
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        t.join();
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE*2);
+        for (int i = 0; i < SIZE + 2; ++i) {
+            for (int j = 0; j < SIZE; j++)
+                assertTrue(q.offer(new Integer(j)));
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(k, l.size());
+            assertEquals(SIZE-k, q.size());
+            for (int j = 0; j < k; ++j)
+                assertEquals(l.get(j), new Integer(j));
+            while (q.poll() != null) ;
+        }
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/PriorityQueueTest.java b/jsr166-tests/src/test/java/jsr166/PriorityQueueTest.java
new file mode 100644
index 0000000..2b237dd
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/PriorityQueueTest.java
@@ -0,0 +1,492 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.PriorityQueue;
+import java.util.Queue;
+
+public class PriorityQueueTest extends JSR166TestCase {
+
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private PriorityQueue<Integer> populatedQueue(int n) {
+        PriorityQueue<Integer> q = new PriorityQueue<Integer>(n);
+        assertTrue(q.isEmpty());
+        for (int i = n-1; i >= 0; i-=2)
+            assertTrue(q.offer(new Integer(i)));
+        for (int i = (n & 1); i < n; i+=2)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * A new queue has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(0, new PriorityQueue(SIZE).size());
+    }
+
+    /**
+     * Constructor throws IAE if capacity argument nonpositive
+     */
+    public void testConstructor2() {
+        try {
+            PriorityQueue q = new PriorityQueue(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            PriorityQueue q = new PriorityQueue((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            Integer[] ints = new Integer[SIZE];
+            PriorityQueue q = new PriorityQueue(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        try {
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i);
+            PriorityQueue q = new PriorityQueue(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        PriorityQueue q = new PriorityQueue(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * The comparator used in constructor is used
+     */
+    public void testConstructor7() {
+        MyReverseComparator cmp = new MyReverseComparator();
+        PriorityQueue q = new PriorityQueue(SIZE, cmp);
+        assertEquals(cmp, q.comparator());
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        q.addAll(Arrays.asList(ints));
+        for (int i = SIZE-1; i >= 0; --i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        PriorityQueue q = new PriorityQueue(2);
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * offer(null) throws NPE
+     */
+    public void testOfferNull() {
+        try {
+            PriorityQueue q = new PriorityQueue(1);
+            q.offer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        try {
+            PriorityQueue q = new PriorityQueue(1);
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Offer of comparable element succeeds
+     */
+    public void testOffer() {
+        PriorityQueue q = new PriorityQueue(1);
+        assertTrue(q.offer(zero));
+        assertTrue(q.offer(one));
+    }
+
+    /**
+     * Offer of non-Comparable throws CCE
+     */
+    public void testOfferNonComparable() {
+        try {
+            PriorityQueue q = new PriorityQueue(1);
+            q.offer(new Object());
+            q.offer(new Object());
+            q.offer(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * add of comparable succeeds
+     */
+    public void testAdd() {
+        PriorityQueue q = new PriorityQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            assertTrue(q.add(new Integer(i)));
+        }
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        try {
+            PriorityQueue q = new PriorityQueue(1);
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        try {
+            PriorityQueue q = new PriorityQueue(SIZE);
+            Integer[] ints = new Integer[SIZE];
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        try {
+            PriorityQueue q = new PriorityQueue(SIZE);
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i);
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE-1-i);
+        PriorityQueue q = new PriorityQueue(SIZE);
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.poll());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i-1));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i+1));
+            assertFalse(q.contains(i+1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        PriorityQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        PriorityQueue q = populatedQueue(SIZE);
+        PriorityQueue p = new PriorityQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        PriorityQueue q = populatedQueue(SIZE);
+        PriorityQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE-i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            PriorityQueue q = populatedQueue(SIZE);
+            PriorityQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE-i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer I = (Integer)(p.remove());
+                assertFalse(q.contains(I));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements
+     */
+    public void testToArray() {
+        PriorityQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        Arrays.sort(o);
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements
+     */
+    public void testToArray2() {
+        PriorityQueue<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        Arrays.sort(ints);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.poll());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        PriorityQueue q = populatedQueue(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final PriorityQueue q = new PriorityQueue(3);
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        PriorityQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized queue has same elements
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+}
diff --git a/jsr166-tests/src/test/java/jsr166/RecursiveActionTest.java b/jsr166-tests/src/test/java/jsr166/RecursiveActionTest.java
new file mode 100644
index 0000000..ad61a2e
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/RecursiveActionTest.java
@@ -0,0 +1,1237 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.ForkJoinWorkerThread;
+import java.util.concurrent.RecursiveAction;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import java.util.Arrays;
+import java.util.HashSet;
+
+public class RecursiveActionTest extends JSR166TestCase {
+
+    private static ForkJoinPool mainPool() {
+        return new ForkJoinPool();
+    }
+
+    private static ForkJoinPool singletonPool() {
+        return new ForkJoinPool(1);
+    }
+
+    private static ForkJoinPool asyncSingletonPool() {
+        return new ForkJoinPool(1,
+                                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                                null, true);
+    }
+
+    private void testInvokeOnPool(ForkJoinPool pool, RecursiveAction a) {
+        try {
+            checkNotDone(a);
+
+            assertNull(pool.invoke(a));
+
+            checkCompletedNormally(a);
+        } finally {
+            joinPool(pool);
+        }
+    }
+
+    void checkNotDone(RecursiveAction a) {
+        assertFalse(a.isDone());
+        assertFalse(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertFalse(a.isCancelled());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        if (! ForkJoinTask.inForkJoinPool()) {
+            Thread.currentThread().interrupt();
+            try {
+                a.get();
+                shouldThrow();
+            } catch (InterruptedException success) {
+            } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+            Thread.currentThread().interrupt();
+            try {
+                a.get(5L, SECONDS);
+                shouldThrow();
+            } catch (InterruptedException success) {
+            } catch (Throwable fail) { threadUnexpectedException(fail); }
+        }
+
+        try {
+            a.get(0L, SECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedNormally(RecursiveAction a) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertTrue(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+        assertNull(a.join());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        try {
+            assertNull(a.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertNull(a.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCancelled(RecursiveAction a) {
+        assertTrue(a.isDone());
+        assertTrue(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertTrue(a.getException() instanceof CancellationException);
+        assertNull(a.getRawResult());
+
+        try {
+            a.join();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedAbnormally(RecursiveAction a, Throwable t) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertSame(t.getClass(), a.getException().getClass());
+        assertNull(a.getRawResult());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+
+        try {
+            a.join();
+            shouldThrow();
+        } catch (Throwable expected) {
+            assertSame(expected.getClass(), t.getClass());
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    public static final class FJException extends RuntimeException {
+        public FJException() { super(); }
+        public FJException(Throwable cause) { super(cause); }
+    }
+
+    // A simple recursive action for testing
+    final class FibAction extends CheckedRecursiveAction {
+        final int number;
+        int result;
+        FibAction(int n) { number = n; }
+        protected void realCompute() {
+            int n = number;
+            if (n <= 1)
+                result = n;
+            else {
+                FibAction f1 = new FibAction(n - 1);
+                FibAction f2 = new FibAction(n - 2);
+                invokeAll(f1, f2);
+                result = f1.result + f2.result;
+            }
+        }
+    }
+
+    // A recursive action failing in base case
+    static final class FailingFibAction extends RecursiveAction {
+        final int number;
+        int result;
+        FailingFibAction(int n) { number = n; }
+        public void compute() {
+            int n = number;
+            if (n <= 1)
+                throw new FJException();
+            else {
+                FailingFibAction f1 = new FailingFibAction(n - 1);
+                FailingFibAction f2 = new FailingFibAction(n - 2);
+                invokeAll(f1, f2);
+                result = f1.result + f2.result;
+            }
+        }
+    }
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks. getRawResult of a RecursiveAction returns null;
+     */
+    public void testInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join/quietlyJoin of a forked task succeeds in the presence of interrupts
+     */
+    public void testJoinIgnoresInterrupts() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                final Thread myself = Thread.currentThread();
+
+                // test join()
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                assertNull(f.join());
+                Thread.interrupted();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+
+                f = new FibAction(8);
+                f.cancel(true);
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    Thread.interrupted();
+                    checkCancelled(f);
+                }
+
+                f = new FibAction(8);
+                f.completeExceptionally(new FJException());
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    Thread.interrupted();
+                    checkCompletedAbnormally(f, success);
+                }
+
+                // test quietlyJoin()
+                f = new FibAction(8);
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                f.quietlyJoin();
+                Thread.interrupted();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+
+                f = new FibAction(8);
+                f.cancel(true);
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                f.quietlyJoin();
+                Thread.interrupted();
+                checkCancelled(f);
+
+                f = new FibAction(8);
+                f.completeExceptionally(new FJException());
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                f.quietlyJoin();
+                Thread.interrupted();
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+        a.reinitialize();
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join/quietlyJoin of a forked task when not in ForkJoinPool
+     * succeeds in the presence of interrupts
+     */
+    public void testJoinIgnoresInterruptsOutsideForkJoinPool() {
+        final SynchronousQueue<FibAction[]> sq =
+            new SynchronousQueue<FibAction[]>();
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws InterruptedException {
+                FibAction[] fibActions = new FibAction[6];
+                for (int i = 0; i < fibActions.length; i++)
+                    fibActions[i] = new FibAction(8);
+
+                fibActions[1].cancel(false);
+                fibActions[2].completeExceptionally(new FJException());
+                fibActions[4].cancel(true);
+                fibActions[5].completeExceptionally(new FJException());
+
+                for (int i = 0; i < fibActions.length; i++)
+                    fibActions[i].fork();
+
+                sq.put(fibActions);
+
+                helpQuiesce();
+            }};
+
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                FibAction[] fibActions = sq.take();
+                FibAction f;
+                final Thread myself = Thread.currentThread();
+
+                // test join() ------------
+
+                f = fibActions[0];
+                assertFalse(ForkJoinTask.inForkJoinPool());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                assertNull(f.join());
+                assertTrue(Thread.interrupted());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+
+                f = fibActions[1];
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    assertTrue(Thread.interrupted());
+                    checkCancelled(f);
+                }
+
+                f = fibActions[2];
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    assertTrue(Thread.interrupted());
+                    checkCompletedAbnormally(f, success);
+                }
+
+                // test quietlyJoin() ---------
+
+                f = fibActions[3];
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                f.quietlyJoin();
+                assertTrue(Thread.interrupted());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+
+                f = fibActions[4];
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                f.quietlyJoin();
+                assertTrue(Thread.interrupted());
+                checkCancelled(f);
+
+                f = fibActions[5];
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                f.quietlyJoin();
+                assertTrue(Thread.interrupted());
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+
+        Thread t;
+
+        t = newStartedThread(r);
+        testInvokeOnPool(mainPool(), a);
+        awaitTermination(t, LONG_DELAY_MS);
+
+        a.reinitialize();
+        t = newStartedThread(r);
+        testInvokeOnPool(singletonPool(), a);
+        awaitTermination(t, LONG_DELAY_MS);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(5L, SECONDS));
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPE() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesce() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                assertEquals(21, f.result);
+                assertEquals(0, getQueuedTaskCount());
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, TimeUnit.SECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, SECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * getPool of executing task returns its pool
+     */
+    public void testGetPool() {
+        final ForkJoinPool mainPool = mainPool();
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertSame(mainPool, getPool());
+            }};
+        testInvokeOnPool(mainPool, a);
+    }
+
+    /**
+     * getPool of non-FJ task returns null
+     */
+    public void testGetPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertNull(getPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * inForkJoinPool of executing task returns true
+     */
+    public void testInForkJoinPool() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertTrue(inForkJoinPool());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertFalse(inForkJoinPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * getPool of current thread in pool returns its pool
+     */
+    public void testWorkerGetPool() {
+        final ForkJoinPool mainPool = mainPool();
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                ForkJoinWorkerThread w =
+                    (ForkJoinWorkerThread) Thread.currentThread();
+                assertSame(mainPool, w.getPool());
+            }};
+        testInvokeOnPool(mainPool, a);
+    }
+
+    /**
+     * getPoolIndex of current thread in pool returns 0 <= value < poolSize
+     */
+    public void testWorkerGetPoolIndex() {
+        final ForkJoinPool mainPool = mainPool();
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                ForkJoinWorkerThread w =
+                    (ForkJoinWorkerThread) Thread.currentThread();
+                assertTrue(w.getPoolIndex() >= 0);
+                // pool size can shrink after assigning index, so cannot check
+                // assertTrue(w.getPoolIndex() < mainPool.getPoolSize());
+            }};
+        testInvokeOnPool(mainPool, a);
+    }
+
+    /**
+     * setRawResult(null) succeeds
+     */
+    public void testSetRawResult() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                setRawResult(null);
+                assertNull(getRawResult());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * A reinitialized normally completed task may be re-invoked
+     */
+    public void testReinitialize() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                checkNotDone(f);
+
+                for (int i = 0; i < 3; i++) {
+                    assertNull(f.invoke());
+                    assertEquals(21, f.result);
+                    checkCompletedNormally(f);
+                    f.reinitialize();
+                    checkNotDone(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * A reinitialized abnormally completed task may be re-invoked
+     */
+    public void testReinitializeAbnormal() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                checkNotDone(f);
+
+                for (int i = 0; i < 3; i++) {
+                    try {
+                        f.invoke();
+                        shouldThrow();
+                    } catch (FJException success) {
+                        checkCompletedAbnormally(f, success);
+                    }
+                    f.reinitialize();
+                    checkNotDone(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                f.completeExceptionally(new FJException());
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task suppresses execution invoking complete
+     */
+    public void testComplete() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                f.complete(null);
+                assertNull(f.invoke());
+                assertEquals(0, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                invokeAll(f, g);
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+                checkCompletedNormally(g);
+                assertEquals(34, g.result);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = new FibAction(7);
+                invokeAll(f, g, h);
+                assertTrue(f.isDone());
+                assertTrue(g.isDone());
+                assertTrue(h.isDone());
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+                checkCompletedNormally(g);
+                assertEquals(34, g.result);
+                checkCompletedNormally(g);
+                assertEquals(13, h.result);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollection() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = new FibAction(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertTrue(f.isDone());
+                assertTrue(g.isDone());
+                assertTrue(h.isDone());
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+                checkCompletedNormally(g);
+                assertEquals(34, g.result);
+                checkCompletedNormally(g);
+                assertEquals(13, h.result);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPE() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FailingFibAction g = new FailingFibAction(9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction g = new FailingFibAction(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FailingFibAction g = new FailingFibAction(9);
+                FibAction h = new FibAction(7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollection() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = new FibAction(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * tryUnfork returns true for most recent unexecuted task,
+     * and suppresses execution
+     */
+    public void testTryUnfork() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertTrue(f.tryUnfork());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * getSurplusQueuedTaskCount returns > 0 when
+     * there are more tasks than threads
+     */
+    public void testGetSurplusQueuedTaskCount() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction h = new FibAction(7);
+                assertSame(h, h.fork());
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertTrue(getSurplusQueuedTaskCount() > 0);
+                helpQuiesce();
+                assertEquals(0, getSurplusQueuedTaskCount());
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns most recent unexecuted task.
+     */
+    public void testPeekNextLocalTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(f, peekNextLocalTask());
+                assertNull(f.join());
+                checkCompletedNormally(f);
+                helpQuiesce();
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns most recent unexecuted task
+     * without executing it
+     */
+    public void testPollNextLocalTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollNextLocalTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it
+     */
+    public void testPollTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns least recent unexecuted task in async mode
+     */
+    public void testPeekNextLocalTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(g, peekNextLocalTask());
+                assertNull(f.join());
+                helpQuiesce();
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns least recent unexecuted task without
+     * executing it, in async mode
+     */
+    public void testPollNextLocalTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollNextLocalTask());
+                helpQuiesce();
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it, in
+     * async mode
+     */
+    public void testPollTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollTask());
+                helpQuiesce();
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /** Demo from RecursiveAction javadoc */
+    static class SortTask extends RecursiveAction {
+        final long[] array; final int lo, hi;
+        SortTask(long[] array, int lo, int hi) {
+            this.array = array; this.lo = lo; this.hi = hi;
+        }
+        SortTask(long[] array) { this(array, 0, array.length); }
+        protected void compute() {
+            if (hi - lo < THRESHOLD)
+                sortSequentially(lo, hi);
+            else {
+                int mid = (lo + hi) >>> 1;
+                invokeAll(new SortTask(array, lo, mid),
+                          new SortTask(array, mid, hi));
+                merge(lo, mid, hi);
+            }
+        }
+        // implementation details follow:
+        static final int THRESHOLD = 100;
+        void sortSequentially(int lo, int hi) {
+            Arrays.sort(array, lo, hi);
+        }
+        void merge(int lo, int mid, int hi) {
+            long[] buf = Arrays.copyOfRange(array, lo, mid);
+            for (int i = 0, j = lo, k = mid; i < buf.length; j++)
+                array[j] = (k == hi || buf[i] < array[k]) ?
+                    buf[i++] : array[k++];
+        }
+    }
+
+    /**
+     * SortTask demo works as advertised
+     */
+    public void testSortTaskDemo() {
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        long[] array = new long[1007];
+        for (int i = 0; i < array.length; i++)
+            array[i] = rnd.nextLong();
+        long[] arrayClone = array.clone();
+        testInvokeOnPool(mainPool(), new SortTask(array));
+        Arrays.sort(arrayClone);
+        assertTrue(Arrays.equals(array, arrayClone));
+    }
+}
diff --git a/jsr166-tests/src/test/java/jsr166/RecursiveTaskTest.java b/jsr166-tests/src/test/java/jsr166/RecursiveTaskTest.java
new file mode 100644
index 0000000..48b6470
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/RecursiveTaskTest.java
@@ -0,0 +1,1020 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.ForkJoinWorkerThread;
+import java.util.concurrent.RecursiveTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import java.util.HashSet;
+
+public class RecursiveTaskTest extends JSR166TestCase {
+
+    private static ForkJoinPool mainPool() {
+        return new ForkJoinPool();
+    }
+
+    private static ForkJoinPool singletonPool() {
+        return new ForkJoinPool(1);
+    }
+
+    private static ForkJoinPool asyncSingletonPool() {
+        return new ForkJoinPool(1,
+                                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                                null, true);
+    }
+
+    private <T> T testInvokeOnPool(ForkJoinPool pool, RecursiveTask<T> a) {
+        try {
+            checkNotDone(a);
+
+            T result = pool.invoke(a);
+
+            checkCompletedNormally(a, result);
+            return result;
+        } finally {
+            joinPool(pool);
+        }
+    }
+
+    void checkNotDone(RecursiveTask a) {
+        assertFalse(a.isDone());
+        assertFalse(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertFalse(a.isCancelled());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        if (! ForkJoinTask.inForkJoinPool()) {
+            Thread.currentThread().interrupt();
+            try {
+                a.get();
+                shouldThrow();
+            } catch (InterruptedException success) {
+            } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+            Thread.currentThread().interrupt();
+            try {
+                a.get(5L, SECONDS);
+                shouldThrow();
+            } catch (InterruptedException success) {
+            } catch (Throwable fail) { threadUnexpectedException(fail); }
+        }
+
+        try {
+            a.get(0L, SECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    <T> void checkCompletedNormally(RecursiveTask<T> a, T expected) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertTrue(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertNull(a.getException());
+        assertSame(expected, a.getRawResult());
+        assertSame(expected, a.join());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        try {
+            assertSame(expected, a.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertSame(expected, a.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    /**
+     * Waits for the task to complete, and checks that when it does,
+     * it will have an Integer result equals to the given int.
+     */
+    void checkCompletesNormally(RecursiveTask<Integer> a, int expected) {
+        Integer r = a.join();
+        assertEquals(expected, (int) r);
+        checkCompletedNormally(a, r);
+    }
+
+    /**
+     * Like checkCompletesNormally, but verifies that the task has
+     * already completed.
+     */
+    void checkCompletedNormally(RecursiveTask<Integer> a, int expected) {
+        Integer r = a.getRawResult();
+        assertEquals(expected, (int) r);
+        checkCompletedNormally(a, r);
+    }
+
+    void checkCancelled(RecursiveTask a) {
+        assertTrue(a.isDone());
+        assertTrue(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertTrue(a.getException() instanceof CancellationException);
+        assertNull(a.getRawResult());
+
+        try {
+            a.join();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedAbnormally(RecursiveTask a, Throwable t) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertSame(t.getClass(), a.getException().getClass());
+        assertNull(a.getRawResult());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+
+        try {
+            a.join();
+            shouldThrow();
+        } catch (Throwable expected) {
+            assertSame(t.getClass(), expected.getClass());
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    public static final class FJException extends RuntimeException {
+        public FJException() { super(); }
+    }
+
+    // An invalid return value for Fib
+    static final Integer NoResult = Integer.valueOf(-17);
+
+    // A simple recursive task for testing
+    final class FibTask extends CheckedRecursiveTask<Integer> {
+        final int number;
+        FibTask(int n) { number = n; }
+        public Integer realCompute() {
+            int n = number;
+            if (n <= 1)
+                return n;
+            FibTask f1 = new FibTask(n - 1);
+            f1.fork();
+            return (new FibTask(n - 2)).compute() + f1.join();
+        }
+
+        public void publicSetRawResult(Integer result) {
+            setRawResult(result);
+        }
+    }
+
+    // A recursive action failing in base case
+    final class FailingFibTask extends RecursiveTask<Integer> {
+        final int number;
+        int result;
+        FailingFibTask(int n) { number = n; }
+        public Integer compute() {
+            int n = number;
+            if (n <= 1)
+                throw new FJException();
+            FailingFibTask f1 = new FailingFibTask(n - 1);
+            f1.fork();
+            return (new FibTask(n - 2)).compute() + f1.join();
+        }
+    }
+
+    /**
+     * invoke returns value when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks. getRawResult of a completed non-null task
+     * returns value;
+     */
+    public void testInvoke() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                Integer r = f.invoke();
+                assertEquals(21, (int) r);
+                checkCompletedNormally(f, r);
+                return r;
+            }};
+        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvoke() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                f.quietlyInvoke();
+                checkCompletedNormally(f, 21);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                Integer r = f.join();
+                assertEquals(21, (int) r);
+                checkCompletedNormally(f, r);
+                return r;
+            }};
+        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                Integer r = f.get();
+                assertEquals(21, (int) r);
+                checkCompletedNormally(f, r);
+                return r;
+            }};
+        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                Integer r = f.get(5L, SECONDS);
+                assertEquals(21, (int) r);
+                checkCompletedNormally(f, r);
+                return r;
+            }};
+        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                Integer r = f.getRawResult();
+                assertEquals(21, (int) r);
+                checkCompletedNormally(f, r);
+                return r;
+            }};
+        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesce() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                assertEquals(0, getQueuedTaskCount());
+                checkCompletedNormally(f, 21);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvoke() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvoke() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FailingFibTask f = new FailingFibTask(8);
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FailingFibTask f = new FailingFibTask(8);
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.get(5L, SECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvoke() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertTrue(f.cancel(true));
+                try {
+                    Integer r = f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FibTask f = new FibTask(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FibTask f = new FibTask(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.get(5L, SECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * getPool of executing task returns its pool
+     */
+    public void testGetPool() {
+        final ForkJoinPool mainPool = mainPool();
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                assertSame(mainPool, getPool());
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool, a));
+    }
+
+    /**
+     * getPool of non-FJ task returns null
+     */
+    public void testGetPool2() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                assertNull(getPool());
+                return NoResult;
+            }};
+        assertSame(NoResult, a.invoke());
+    }
+
+    /**
+     * inForkJoinPool of executing task returns true
+     */
+    public void testInForkJoinPool() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                assertTrue(inForkJoinPool());
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                assertFalse(inForkJoinPool());
+                return NoResult;
+            }};
+        assertSame(NoResult, a.invoke());
+    }
+
+    /**
+     * The value set by setRawResult is returned by getRawResult
+     */
+    public void testSetRawResult() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                setRawResult(NoResult);
+                assertSame(NoResult, getRawResult());
+                return NoResult;
+            }
+        };
+        assertSame(NoResult, a.invoke());
+    }
+
+    /**
+     * A reinitialized normally completed task may be re-invoked
+     */
+    public void testReinitialize() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                checkNotDone(f);
+
+                for (int i = 0; i < 3; i++) {
+                    Integer r = f.invoke();
+                    assertEquals(21, (int) r);
+                    checkCompletedNormally(f, r);
+                    f.reinitialize();
+                    f.publicSetRawResult(null);
+                    checkNotDone(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * A reinitialized abnormally completed task may be re-invoked
+     */
+    public void testReinitializeAbnormal() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                checkNotDone(f);
+
+                for (int i = 0; i < 3; i++) {
+                    try {
+                        f.invoke();
+                        shouldThrow();
+                    } catch (FJException success) {
+                        checkCompletedAbnormally(f, success);
+                    }
+                    f.reinitialize();
+                    checkNotDone(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                f.completeExceptionally(new FJException());
+                try {
+                    Integer r = f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invoke task suppresses execution invoking complete
+     */
+    public void testComplete() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                f.complete(NoResult);
+                Integer r = f.invoke();
+                assertSame(NoResult, r);
+                checkCompletedNormally(f, NoResult);
+                return r;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FibTask g = new FibTask(9);
+                invokeAll(f, g);
+                checkCompletedNormally(f, 21);
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                invokeAll(f);
+                checkCompletedNormally(f, 21);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FibTask g = new FibTask(9);
+                FibTask h = new FibTask(7);
+                invokeAll(f, g, h);
+                assertTrue(f.isDone());
+                assertTrue(g.isDone());
+                assertTrue(h.isDone());
+                checkCompletedNormally(f, 21);
+                checkCompletedNormally(g, 34);
+                checkCompletedNormally(h, 13);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollection() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FibTask g = new FibTask(9);
+                FibTask h = new FibTask(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertTrue(f.isDone());
+                assertTrue(g.isDone());
+                assertTrue(h.isDone());
+                checkCompletedNormally(f, 21);
+                checkCompletedNormally(g, 34);
+                checkCompletedNormally(h, 13);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPE() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FibTask g = new FibTask(9);
+                FibTask h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FailingFibTask g = new FailingFibTask(9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask g = new FailingFibTask(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FailingFibTask g = new FailingFibTask(9);
+                FibTask h = new FibTask(7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollection() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                FibTask g = new FibTask(9);
+                FibTask h = new FibTask(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * tryUnfork returns true for most recent unexecuted task,
+     * and suppresses execution
+     */
+    public void testTryUnfork() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertTrue(f.tryUnfork());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
+    }
+
+    /**
+     * getSurplusQueuedTaskCount returns > 0 when
+     * there are more tasks than threads
+     */
+    public void testGetSurplusQueuedTaskCount() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask h = new FibTask(7);
+                assertSame(h, h.fork());
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertTrue(getSurplusQueuedTaskCount() > 0);
+                helpQuiesce();
+                assertEquals(0, getSurplusQueuedTaskCount());
+                checkCompletedNormally(f, 21);
+                checkCompletedNormally(g, 34);
+                checkCompletedNormally(h, 13);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
+    }
+
+    /**
+     * peekNextLocalTask returns most recent unexecuted task.
+     */
+    public void testPeekNextLocalTask() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(f, peekNextLocalTask());
+                checkCompletesNormally(f, 21);
+                helpQuiesce();
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
+    }
+
+    /**
+     * pollNextLocalTask returns most recent unexecuted task
+     * without executing it
+     */
+    public void testPollNextLocalTask() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollNextLocalTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it
+     */
+    public void testPollTask() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
+    }
+
+    /**
+     * peekNextLocalTask returns least recent unexecuted task in async mode
+     */
+    public void testPeekNextLocalTaskAsync() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(g, peekNextLocalTask());
+                assertEquals(21, (int) f.join());
+                helpQuiesce();
+                checkCompletedNormally(f, 21);
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(asyncSingletonPool(), a));
+    }
+
+    /**
+     * pollNextLocalTask returns least recent unexecuted task without
+     * executing it, in async mode
+     */
+    public void testPollNextLocalTaskAsync() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollNextLocalTask());
+                helpQuiesce();
+                checkCompletedNormally(f, 21);
+                checkNotDone(g);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(asyncSingletonPool(), a));
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it, in
+     * async mode
+     */
+    public void testPollTaskAsync() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollTask());
+                helpQuiesce();
+                checkCompletedNormally(f, 21);
+                checkNotDone(g);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(asyncSingletonPool(), a));
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ReentrantLockTest.java b/jsr166-tests/src/test/java/jsr166/ReentrantLockTest.java
new file mode 100644
index 0000000..6fe8122
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ReentrantLockTest.java
@@ -0,0 +1,1133 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import java.util.*;
+
+public class ReentrantLockTest extends JSR166TestCase {
+
+    /**
+     * A runnable calling lockInterruptibly
+     */
+    class InterruptibleLockRunnable extends CheckedRunnable {
+        final ReentrantLock lock;
+        InterruptibleLockRunnable(ReentrantLock l) { lock = l; }
+        public void realRun() throws InterruptedException {
+            lock.lockInterruptibly();
+        }
+    }
+
+    /**
+     * A runnable calling lockInterruptibly that expects to be
+     * interrupted
+     */
+    class InterruptedLockRunnable extends CheckedInterruptedRunnable {
+        final ReentrantLock lock;
+        InterruptedLockRunnable(ReentrantLock l) { lock = l; }
+        public void realRun() throws InterruptedException {
+            lock.lockInterruptibly();
+        }
+    }
+
+    /**
+     * Subclass to expose protected methods
+     */
+    static class PublicReentrantLock extends ReentrantLock {
+        PublicReentrantLock() { super(); }
+        PublicReentrantLock(boolean fair) { super(fair); }
+        public Thread getOwner() {
+            return super.getOwner();
+        }
+        public Collection<Thread> getQueuedThreads() {
+            return super.getQueuedThreads();
+        }
+        public Collection<Thread> getWaitingThreads(Condition c) {
+            return super.getWaitingThreads(c);
+        }
+    }
+
+    /**
+     * Releases write lock, checking that it had a hold count of 1.
+     */
+    void releaseLock(PublicReentrantLock lock) {
+        assertLockedByMoi(lock);
+        lock.unlock();
+        assertFalse(lock.isHeldByCurrentThread());
+        assertNotLocked(lock);
+    }
+
+    /**
+     * Spin-waits until lock.hasQueuedThread(t) becomes true.
+     */
+    void waitForQueuedThread(PublicReentrantLock lock, Thread t) {
+        long startTime = System.nanoTime();
+        while (!lock.hasQueuedThread(t)) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                throw new AssertionFailedError("timed out");
+            Thread.yield();
+        }
+        assertTrue(t.isAlive());
+        assertNotSame(t, lock.getOwner());
+    }
+
+    /**
+     * Checks that lock is not locked.
+     */
+    void assertNotLocked(PublicReentrantLock lock) {
+        assertFalse(lock.isLocked());
+        assertFalse(lock.isHeldByCurrentThread());
+        assertNull(lock.getOwner());
+        assertEquals(0, lock.getHoldCount());
+    }
+
+    /**
+     * Checks that lock is locked by the given thread.
+     */
+    void assertLockedBy(PublicReentrantLock lock, Thread t) {
+        assertTrue(lock.isLocked());
+        assertSame(t, lock.getOwner());
+        assertEquals(t == Thread.currentThread(),
+                     lock.isHeldByCurrentThread());
+        assertEquals(t == Thread.currentThread(),
+                     lock.getHoldCount() > 0);
+    }
+
+    /**
+     * Checks that lock is locked by the current thread.
+     */
+    void assertLockedByMoi(PublicReentrantLock lock) {
+        assertLockedBy(lock, Thread.currentThread());
+    }
+
+    /**
+     * Checks that condition c has no waiters.
+     */
+    void assertHasNoWaiters(PublicReentrantLock lock, Condition c) {
+        assertHasWaiters(lock, c, new Thread[] {});
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads.
+     */
+    void assertHasWaiters(PublicReentrantLock lock, Condition c,
+                          Thread... threads) {
+        lock.lock();
+        assertEquals(threads.length > 0, lock.hasWaiters(c));
+        assertEquals(threads.length, lock.getWaitQueueLength(c));
+        assertEquals(threads.length == 0, lock.getWaitingThreads(c).isEmpty());
+        assertEquals(threads.length, lock.getWaitingThreads(c).size());
+        assertEquals(new HashSet<Thread>(lock.getWaitingThreads(c)),
+                     new HashSet<Thread>(Arrays.asList(threads)));
+        lock.unlock();
+    }
+
+    enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil };
+
+    /**
+     * Awaits condition using the specified AwaitMethod.
+     */
+    void await(Condition c, AwaitMethod awaitMethod)
+            throws InterruptedException {
+        long timeoutMillis = 2 * LONG_DELAY_MS;
+        switch (awaitMethod) {
+        case await:
+            c.await();
+            break;
+        case awaitTimed:
+            assertTrue(c.await(timeoutMillis, MILLISECONDS));
+            break;
+        case awaitNanos:
+            long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis);
+            long nanosRemaining = c.awaitNanos(nanosTimeout);
+            assertTrue(nanosRemaining > 0);
+            break;
+        case awaitUntil:
+            assertTrue(c.awaitUntil(delayedDate(timeoutMillis)));
+            break;
+        }
+    }
+
+    /**
+     * Constructor sets given fairness, and is in unlocked state
+     */
+    public void testConstructor() {
+        PublicReentrantLock lock;
+
+        lock = new PublicReentrantLock();
+        assertFalse(lock.isFair());
+        assertNotLocked(lock);
+
+        lock = new PublicReentrantLock(true);
+        assertTrue(lock.isFair());
+        assertNotLocked(lock);
+
+        lock = new PublicReentrantLock(false);
+        assertFalse(lock.isFair());
+        assertNotLocked(lock);
+    }
+
+    /**
+     * locking an unlocked lock succeeds
+     */
+    public void testLock()      { testLock(false); }
+    public void testLock_fair() { testLock(true); }
+    public void testLock(boolean fair) {
+        PublicReentrantLock lock = new PublicReentrantLock(fair);
+        lock.lock();
+        assertLockedByMoi(lock);
+        releaseLock(lock);
+    }
+
+    /**
+     * Unlocking an unlocked lock throws IllegalMonitorStateException
+     */
+    public void testUnlock_IMSE()      { testUnlock_IMSE(false); }
+    public void testUnlock_IMSE_fair() { testUnlock_IMSE(true); }
+    public void testUnlock_IMSE(boolean fair) {
+        ReentrantLock lock = new ReentrantLock(fair);
+        try {
+            lock.unlock();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * tryLock on an unlocked lock succeeds
+     */
+    public void testTryLock()      { testTryLock(false); }
+    public void testTryLock_fair() { testTryLock(true); }
+    public void testTryLock(boolean fair) {
+        PublicReentrantLock lock = new PublicReentrantLock(fair);
+        assertTrue(lock.tryLock());
+        assertLockedByMoi(lock);
+        assertTrue(lock.tryLock());
+        assertLockedByMoi(lock);
+        lock.unlock();
+        releaseLock(lock);
+    }
+
+    /**
+     * hasQueuedThreads reports whether there are waiting threads
+     */
+    public void testHasQueuedThreads()      { testHasQueuedThreads(false); }
+    public void testHasQueuedThreads_fair() { testHasQueuedThreads(true); }
+    public void testHasQueuedThreads(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertFalse(lock.hasQueuedThreads());
+        lock.lock();
+        assertFalse(lock.hasQueuedThreads());
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertTrue(lock.hasQueuedThreads());
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertTrue(lock.hasQueuedThreads());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(lock.hasQueuedThreads());
+        lock.unlock();
+        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 PublicReentrantLock lock = new PublicReentrantLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertEquals(0, lock.getQueueLength());
+        lock.lock();
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertEquals(1, lock.getQueueLength());
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertEquals(2, lock.getQueueLength());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertEquals(1, lock.getQueueLength());
+        lock.unlock();
+        awaitTermination(t2);
+        assertEquals(0, lock.getQueueLength());
+    }
+
+    /**
+     * hasQueuedThread(null) throws NPE
+     */
+    public void testHasQueuedThreadNPE()      { testHasQueuedThreadNPE(false); }
+    public void testHasQueuedThreadNPE_fair() { testHasQueuedThreadNPE(true); }
+    public void testHasQueuedThreadNPE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        try {
+            lock.hasQueuedThread(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasQueuedThread reports whether a thread is queued
+     */
+    public void testHasQueuedThread()      { testHasQueuedThread(false); }
+    public void testHasQueuedThread_fair() { testHasQueuedThread(true); }
+    public void testHasQueuedThread(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertFalse(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        lock.lock();
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        lock.unlock();
+        awaitTermination(t2);
+        assertFalse(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+    }
+
+    /**
+     * getQueuedThreads includes waiting threads
+     */
+    public void testGetQueuedThreads()      { testGetQueuedThreads(false); }
+    public void testGetQueuedThreads_fair() { testGetQueuedThreads(true); }
+    public void testGetQueuedThreads(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertTrue(lock.getQueuedThreads().isEmpty());
+        lock.lock();
+        assertTrue(lock.getQueuedThreads().isEmpty());
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertEquals(1, lock.getQueuedThreads().size());
+        assertTrue(lock.getQueuedThreads().contains(t1));
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertEquals(2, lock.getQueuedThreads().size());
+        assertTrue(lock.getQueuedThreads().contains(t1));
+        assertTrue(lock.getQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(lock.getQueuedThreads().contains(t1));
+        assertTrue(lock.getQueuedThreads().contains(t2));
+        assertEquals(1, lock.getQueuedThreads().size());
+        lock.unlock();
+        awaitTermination(t2);
+        assertTrue(lock.getQueuedThreads().isEmpty());
+    }
+
+    /**
+     * timed tryLock is interruptible
+     */
+    public void testTryLock_Interruptible()      { testTryLock_Interruptible(false); }
+    public void testTryLock_Interruptible_fair() { testTryLock_Interruptible(true); }
+    public void testTryLock_Interruptible(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        lock.lock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.tryLock(2 * LONG_DELAY_MS, MILLISECONDS);
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseLock(lock);
+    }
+
+    /**
+     * tryLock on a locked lock fails
+     */
+    public void testTryLockWhenLocked()      { testTryLockWhenLocked(false); }
+    public void testTryLockWhenLocked_fair() { testTryLockWhenLocked(true); }
+    public void testTryLockWhenLocked(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        lock.lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(lock.tryLock());
+            }});
+
+        awaitTermination(t);
+        releaseLock(lock);
+    }
+
+    /**
+     * Timed tryLock on a locked lock times out
+     */
+    public void testTryLock_Timeout()      { testTryLock_Timeout(false); }
+    public void testTryLock_Timeout_fair() { testTryLock_Timeout(true); }
+    public void testTryLock_Timeout(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        lock.lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                long timeoutMillis = 10;
+                assertFalse(lock.tryLock(timeoutMillis, MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            }});
+
+        awaitTermination(t);
+        releaseLock(lock);
+    }
+
+    /**
+     * getHoldCount returns number of recursive holds
+     */
+    public void testGetHoldCount()      { testGetHoldCount(false); }
+    public void testGetHoldCount_fair() { testGetHoldCount(true); }
+    public void testGetHoldCount(boolean fair) {
+        ReentrantLock lock = new ReentrantLock(fair);
+        for (int i = 1; i <= SIZE; i++) {
+            lock.lock();
+            assertEquals(i, lock.getHoldCount());
+        }
+        for (int i = SIZE; i > 0; i--) {
+            lock.unlock();
+            assertEquals(i-1, lock.getHoldCount());
+        }
+    }
+
+    /**
+     * isLocked is true when locked and false when not
+     */
+    public void testIsLocked()      { testIsLocked(false); }
+    public void testIsLocked_fair() { testIsLocked(true); }
+    public void testIsLocked(boolean fair) {
+        try {
+            final ReentrantLock lock = new ReentrantLock(fair);
+            assertFalse(lock.isLocked());
+            lock.lock();
+            assertTrue(lock.isLocked());
+            lock.lock();
+            assertTrue(lock.isLocked());
+            lock.unlock();
+            assertTrue(lock.isLocked());
+            lock.unlock();
+            assertFalse(lock.isLocked());
+            final CyclicBarrier barrier = new CyclicBarrier(2);
+            Thread t = newStartedThread(new CheckedRunnable() {
+                    public void realRun() throws Exception {
+                        lock.lock();
+                        assertTrue(lock.isLocked());
+                        barrier.await();
+                        barrier.await();
+                        lock.unlock();
+                    }});
+
+            barrier.await();
+            assertTrue(lock.isLocked());
+            barrier.await();
+            awaitTermination(t);
+            assertFalse(lock.isLocked());
+        } catch (Exception e) {
+            threadUnexpectedException(e);
+        }
+    }
+
+    /**
+     * lockInterruptibly succeeds when unlocked, else is interruptible
+     */
+    public void testLockInterruptibly()      { testLockInterruptibly(false); }
+    public void testLockInterruptibly_fair() { testLockInterruptibly(true); }
+    public void testLockInterruptibly(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        try {
+            lock.lockInterruptibly();
+        } catch (InterruptedException ie) {
+            threadUnexpectedException(ie);
+        }
+        assertLockedByMoi(lock);
+        Thread t = newStartedThread(new InterruptedLockRunnable(lock));
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        assertTrue(lock.isLocked());
+        assertTrue(lock.isHeldByCurrentThread());
+        awaitTermination(t);
+        releaseLock(lock);
+    }
+
+    /**
+     * Calling await without holding lock throws IllegalMonitorStateException
+     */
+    public void testAwait_IMSE()      { testAwait_IMSE(false); }
+    public void testAwait_IMSE_fair() { testAwait_IMSE(true); }
+    public void testAwait_IMSE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        for (AwaitMethod awaitMethod : AwaitMethod.values()) {
+            long startTime = System.nanoTime();
+            try {
+                await(c, awaitMethod);
+                shouldThrow();
+            } catch (IllegalMonitorStateException success) {
+            } catch (InterruptedException e) { threadUnexpectedException(e); }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * Calling signal without holding lock throws IllegalMonitorStateException
+     */
+    public void testSignal_IMSE()      { testSignal_IMSE(false); }
+    public void testSignal_IMSE_fair() { testSignal_IMSE(true); }
+    public void testSignal_IMSE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        try {
+            c.signal();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * awaitNanos without a signal times out
+     */
+    public void testAwaitNanos_Timeout()      { testAwaitNanos_Timeout(false); }
+    public void testAwaitNanos_Timeout_fair() { testAwaitNanos_Timeout(true); }
+    public void testAwaitNanos_Timeout(boolean fair) {
+        try {
+            final ReentrantLock lock = new ReentrantLock(fair);
+            final Condition c = lock.newCondition();
+            lock.lock();
+            long startTime = System.nanoTime();
+            long timeoutMillis = 10;
+            long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis);
+            long nanosRemaining = c.awaitNanos(timeoutNanos);
+            assertTrue(nanosRemaining <= 0);
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            lock.unlock();
+        } catch (InterruptedException e) {
+            threadUnexpectedException(e);
+        }
+    }
+
+    /**
+     * timed await without a signal times out
+     */
+    public void testAwait_Timeout()      { testAwait_Timeout(false); }
+    public void testAwait_Timeout_fair() { testAwait_Timeout(true); }
+    public void testAwait_Timeout(boolean fair) {
+        try {
+            final ReentrantLock lock = new ReentrantLock(fair);
+            final Condition c = lock.newCondition();
+            lock.lock();
+            long startTime = System.nanoTime();
+            long timeoutMillis = 10;
+            assertFalse(c.await(timeoutMillis, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            lock.unlock();
+        } catch (InterruptedException e) {
+            threadUnexpectedException(e);
+        }
+    }
+
+    /**
+     * awaitUntil without a signal times out
+     */
+    public void testAwaitUntil_Timeout()      { testAwaitUntil_Timeout(false); }
+    public void testAwaitUntil_Timeout_fair() { testAwaitUntil_Timeout(true); }
+    public void testAwaitUntil_Timeout(boolean fair) {
+        try {
+            final ReentrantLock lock = new ReentrantLock(fair);
+            final Condition c = lock.newCondition();
+            lock.lock();
+            long startTime = System.nanoTime();
+            long timeoutMillis = 10;
+            java.util.Date d = new java.util.Date();
+            assertFalse(c.awaitUntil(new java.util.Date(d.getTime() + timeoutMillis)));
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            lock.unlock();
+        } catch (InterruptedException e) {
+            threadUnexpectedException(e);
+        }
+    }
+
+    /**
+     * await returns when signalled
+     */
+    public void testAwait()      { testAwait(false); }
+    public void testAwait_fair() { testAwait(true); }
+    public void testAwait(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch locked = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                locked.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        await(locked);
+        lock.lock();
+        assertHasWaiters(lock, c, t);
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertTrue(t.isAlive());
+        lock.unlock();
+        awaitTermination(t);
+    }
+
+    /**
+     * hasWaiters throws NPE if null
+     */
+    public void testHasWaitersNPE()      { testHasWaitersNPE(false); }
+    public void testHasWaitersNPE_fair() { testHasWaitersNPE(true); }
+    public void testHasWaitersNPE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        try {
+            lock.hasWaiters(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws NPE if null
+     */
+    public void testGetWaitQueueLengthNPE()      { testGetWaitQueueLengthNPE(false); }
+    public void testGetWaitQueueLengthNPE_fair() { testGetWaitQueueLengthNPE(true); }
+    public void testGetWaitQueueLengthNPE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        try {
+            lock.getWaitQueueLength(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws NPE if null
+     */
+    public void testGetWaitingThreadsNPE()      { testGetWaitingThreadsNPE(false); }
+    public void testGetWaitingThreadsNPE_fair() { testGetWaitingThreadsNPE(true); }
+    public void testGetWaitingThreadsNPE(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        try {
+            lock.getWaitingThreads(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalArgumentException if not owned
+     */
+    public void testHasWaitersIAE()      { testHasWaitersIAE(false); }
+    public void testHasWaitersIAE_fair() { testHasWaitersIAE(true); }
+    public void testHasWaitersIAE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final ReentrantLock lock2 = new ReentrantLock(fair);
+        try {
+            lock2.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalMonitorStateException if not locked
+     */
+    public void testHasWaitersIMSE()      { testHasWaitersIMSE(false); }
+    public void testHasWaitersIMSE_fair() { testHasWaitersIMSE(true); }
+    public void testHasWaitersIMSE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        try {
+            lock.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitQueueLengthIAE()      { testGetWaitQueueLengthIAE(false); }
+    public void testGetWaitQueueLengthIAE_fair() { testGetWaitQueueLengthIAE(true); }
+    public void testGetWaitQueueLengthIAE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final ReentrantLock lock2 = new ReentrantLock(fair);
+        try {
+            lock2.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalMonitorStateException if not locked
+     */
+    public void testGetWaitQueueLengthIMSE()      { testGetWaitQueueLengthIMSE(false); }
+    public void testGetWaitQueueLengthIMSE_fair() { testGetWaitQueueLengthIMSE(true); }
+    public void testGetWaitQueueLengthIMSE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        try {
+            lock.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitingThreadsIAE()      { testGetWaitingThreadsIAE(false); }
+    public void testGetWaitingThreadsIAE_fair() { testGetWaitingThreadsIAE(true); }
+    public void testGetWaitingThreadsIAE(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final PublicReentrantLock lock2 = new PublicReentrantLock(fair);
+        try {
+            lock2.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws IllegalMonitorStateException if not locked
+     */
+    public void testGetWaitingThreadsIMSE()      { testGetWaitingThreadsIMSE(false); }
+    public void testGetWaitingThreadsIMSE_fair() { testGetWaitingThreadsIMSE(true); }
+    public void testGetWaitingThreadsIMSE(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        try {
+            lock.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * hasWaiters returns true when a thread is waiting, else false
+     */
+    public void testHasWaiters()      { testHasWaiters(false); }
+    public void testHasWaiters_fair() { testHasWaiters(true); }
+    public void testHasWaiters(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch pleaseSignal = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertHasNoWaiters(lock, c);
+                assertFalse(lock.hasWaiters(c));
+                pleaseSignal.countDown();
+                c.await();
+                assertHasNoWaiters(lock, c);
+                assertFalse(lock.hasWaiters(c));
+                lock.unlock();
+            }});
+
+        await(pleaseSignal);
+        lock.lock();
+        assertHasWaiters(lock, c, t);
+        assertTrue(lock.hasWaiters(c));
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertFalse(lock.hasWaiters(c));
+        lock.unlock();
+        awaitTermination(t);
+        assertHasNoWaiters(lock, c);
+    }
+
+    /**
+     * getWaitQueueLength returns number of waiting threads
+     */
+    public void testGetWaitQueueLength()      { testGetWaitQueueLength(false); }
+    public void testGetWaitQueueLength_fair() { testGetWaitQueueLength(true); }
+    public void testGetWaitQueueLength(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch locked1 = new CountDownLatch(1);
+        final CountDownLatch locked2 = new CountDownLatch(1);
+        Thread t1 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertFalse(lock.hasWaiters(c));
+                assertEquals(0, lock.getWaitQueueLength(c));
+                locked1.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        Thread t2 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertTrue(lock.hasWaiters(c));
+                assertEquals(1, lock.getWaitQueueLength(c));
+                locked2.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        lock.lock();
+        assertEquals(0, lock.getWaitQueueLength(c));
+        lock.unlock();
+
+        t1.start();
+        await(locked1);
+
+        lock.lock();
+        assertHasWaiters(lock, c, t1);
+        assertEquals(1, lock.getWaitQueueLength(c));
+        lock.unlock();
+
+        t2.start();
+        await(locked2);
+
+        lock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        assertEquals(2, lock.getWaitQueueLength(c));
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.unlock();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+
+        assertHasNoWaiters(lock, c);
+    }
+
+    /**
+     * getWaitingThreads returns only and all waiting threads
+     */
+    public void testGetWaitingThreads()      { testGetWaitingThreads(false); }
+    public void testGetWaitingThreads_fair() { testGetWaitingThreads(true); }
+    public void testGetWaitingThreads(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch locked1 = new CountDownLatch(1);
+        final CountDownLatch locked2 = new CountDownLatch(1);
+        Thread t1 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertTrue(lock.getWaitingThreads(c).isEmpty());
+                locked1.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        Thread t2 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertFalse(lock.getWaitingThreads(c).isEmpty());
+                locked2.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        lock.lock();
+        assertTrue(lock.getWaitingThreads(c).isEmpty());
+        lock.unlock();
+
+        t1.start();
+        await(locked1);
+
+        lock.lock();
+        assertHasWaiters(lock, c, t1);
+        assertTrue(lock.getWaitingThreads(c).contains(t1));
+        assertFalse(lock.getWaitingThreads(c).contains(t2));
+        assertEquals(1, lock.getWaitingThreads(c).size());
+        lock.unlock();
+
+        t2.start();
+        await(locked2);
+
+        lock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        assertTrue(lock.getWaitingThreads(c).contains(t1));
+        assertTrue(lock.getWaitingThreads(c).contains(t2));
+        assertEquals(2, lock.getWaitingThreads(c).size());
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.unlock();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+
+        assertHasNoWaiters(lock, c);
+    }
+
+    /**
+     * awaitUninterruptibly is uninterruptible
+     */
+    public void testAwaitUninterruptibly()      { testAwaitUninterruptibly(false); }
+    public void testAwaitUninterruptibly_fair() { testAwaitUninterruptibly(true); }
+    public void testAwaitUninterruptibly(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(2);
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                // Interrupt before awaitUninterruptibly
+                lock.lock();
+                pleaseInterrupt.countDown();
+                Thread.currentThread().interrupt();
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                lock.unlock();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                // Interrupt during awaitUninterruptibly
+                lock.lock();
+                pleaseInterrupt.countDown();
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                lock.unlock();
+            }});
+
+        await(pleaseInterrupt);
+        lock.lock();
+        lock.unlock();
+        t2.interrupt();
+
+        assertThreadStaysAlive(t1);
+        assertTrue(t2.isAlive());
+
+        lock.lock();
+        c.signalAll();
+        lock.unlock();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil is interruptible
+     */
+    public void testInterruptible_await()           { testInterruptible(false, AwaitMethod.await); }
+    public void testInterruptible_await_fair()      { testInterruptible(true,  AwaitMethod.await); }
+    public void testInterruptible_awaitTimed()      { testInterruptible(false, AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitTimed_fair() { testInterruptible(true,  AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitNanos()      { testInterruptible(false, AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitNanos_fair() { testInterruptible(true,  AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitUntil()      { testInterruptible(false, AwaitMethod.awaitUntil); }
+    public void testInterruptible_awaitUntil_fair() { testInterruptible(true,  AwaitMethod.awaitUntil); }
+    public void testInterruptible(boolean fair, final AwaitMethod awaitMethod) {
+        final PublicReentrantLock lock =
+            new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertLockedByMoi(lock);
+                assertHasNoWaiters(lock, c);
+                pleaseInterrupt.countDown();
+                try {
+                    await(c, awaitMethod);
+                } finally {
+                    assertLockedByMoi(lock);
+                    assertHasNoWaiters(lock, c);
+                    lock.unlock();
+                    assertFalse(Thread.interrupted());
+                }
+            }});
+
+        await(pleaseInterrupt);
+        assertHasWaiters(lock, c, t);
+        t.interrupt();
+        awaitTermination(t);
+        assertNotLocked(lock);
+    }
+
+    /**
+     * signalAll wakes up all threads
+     */
+    public void testSignalAll_await()           { testSignalAll(false, AwaitMethod.await); }
+    public void testSignalAll_await_fair()      { testSignalAll(true,  AwaitMethod.await); }
+    public void testSignalAll_awaitTimed()      { testSignalAll(false, AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitTimed_fair() { testSignalAll(true,  AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitNanos()      { testSignalAll(false, AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitNanos_fair() { testSignalAll(true,  AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitUntil()      { testSignalAll(false, AwaitMethod.awaitUntil); }
+    public void testSignalAll_awaitUntil_fair() { testSignalAll(true,  AwaitMethod.awaitUntil); }
+    public void testSignalAll(boolean fair, final AwaitMethod awaitMethod) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch pleaseSignal = new CountDownLatch(2);
+        class Awaiter extends CheckedRunnable {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                pleaseSignal.countDown();
+                await(c, awaitMethod);
+                lock.unlock();
+            }
+        }
+
+        Thread t1 = newStartedThread(new Awaiter());
+        Thread t2 = newStartedThread(new Awaiter());
+
+        await(pleaseSignal);
+        lock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * signal wakes up waiting threads in FIFO order
+     */
+    public void testSignalWakesFifo()      { testSignalWakesFifo(false); }
+    public void testSignalWakesFifo_fair() { testSignalWakesFifo(true); }
+    public void testSignalWakesFifo(boolean fair) {
+        final PublicReentrantLock lock =
+            new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch locked1 = new CountDownLatch(1);
+        final CountDownLatch locked2 = new CountDownLatch(1);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                locked1.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        await(locked1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                locked2.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        await(locked2);
+
+        lock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        assertFalse(lock.hasQueuedThreads());
+        c.signal();
+        assertHasWaiters(lock, c, t2);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        lock.unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * await after multiple reentrant locking preserves lock count
+     */
+    public void testAwaitLockCount()      { testAwaitLockCount(false); }
+    public void testAwaitLockCount_fair() { testAwaitLockCount(true); }
+    public void testAwaitLockCount(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch pleaseSignal = new CountDownLatch(2);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertLockedByMoi(lock);
+                assertEquals(1, lock.getHoldCount());
+                pleaseSignal.countDown();
+                c.await();
+                assertLockedByMoi(lock);
+                assertEquals(1, lock.getHoldCount());
+                lock.unlock();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                lock.lock();
+                assertLockedByMoi(lock);
+                assertEquals(2, lock.getHoldCount());
+                pleaseSignal.countDown();
+                c.await();
+                assertLockedByMoi(lock);
+                assertEquals(2, lock.getHoldCount());
+                lock.unlock();
+                lock.unlock();
+            }});
+
+        await(pleaseSignal);
+        lock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        assertEquals(1, lock.getHoldCount());
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A serialized lock deserializes as unlocked
+     */
+    public void testSerialization()      { testSerialization(false); }
+    public void testSerialization_fair() { testSerialization(true); }
+    public void testSerialization(boolean fair) {
+        ReentrantLock lock = new ReentrantLock(fair);
+        lock.lock();
+
+        ReentrantLock clone = serialClone(lock);
+        assertEquals(lock.isFair(), clone.isFair());
+        assertTrue(lock.isLocked());
+        assertFalse(clone.isLocked());
+        assertEquals(1, lock.getHoldCount());
+        assertEquals(0, clone.getHoldCount());
+        clone.lock();
+        clone.lock();
+        assertTrue(clone.isLocked());
+        assertEquals(2, clone.getHoldCount());
+        assertEquals(1, lock.getHoldCount());
+        clone.unlock();
+        clone.unlock();
+        assertTrue(lock.isLocked());
+        assertFalse(clone.isLocked());
+    }
+
+    /**
+     * toString indicates current lock state
+     */
+    public void testToString()      { testToString(false); }
+    public void testToString_fair() { testToString(true); }
+    public void testToString(boolean fair) {
+        ReentrantLock lock = new ReentrantLock(fair);
+        assertTrue(lock.toString().contains("Unlocked"));
+        lock.lock();
+        assertTrue(lock.toString().contains("Locked"));
+        lock.unlock();
+        assertTrue(lock.toString().contains("Unlocked"));
+    }
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ReentrantReadWriteLockTest.java b/jsr166-tests/src/test/java/jsr166/ReentrantReadWriteLockTest.java
new file mode 100644
index 0000000..2be27d2
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ReentrantReadWriteLockTest.java
@@ -0,0 +1,1670 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.CountDownLatch;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import java.util.*;
+
+public class ReentrantReadWriteLockTest extends JSR166TestCase {
+
+    /**
+     * A runnable calling lockInterruptibly
+     */
+    class InterruptibleLockRunnable extends CheckedRunnable {
+        final ReentrantReadWriteLock lock;
+        InterruptibleLockRunnable(ReentrantReadWriteLock l) { lock = l; }
+        public void realRun() throws InterruptedException {
+            lock.writeLock().lockInterruptibly();
+        }
+    }
+
+    /**
+     * A runnable calling lockInterruptibly that expects to be
+     * interrupted
+     */
+    class InterruptedLockRunnable extends CheckedInterruptedRunnable {
+        final ReentrantReadWriteLock lock;
+        InterruptedLockRunnable(ReentrantReadWriteLock l) { lock = l; }
+        public void realRun() throws InterruptedException {
+            lock.writeLock().lockInterruptibly();
+        }
+    }
+
+    /**
+     * Subclass to expose protected methods
+     */
+    static class PublicReentrantReadWriteLock extends ReentrantReadWriteLock {
+        PublicReentrantReadWriteLock() { super(); }
+        PublicReentrantReadWriteLock(boolean fair) { super(fair); }
+        public Thread getOwner() {
+            return super.getOwner();
+        }
+        public Collection<Thread> getQueuedThreads() {
+            return super.getQueuedThreads();
+        }
+        public Collection<Thread> getWaitingThreads(Condition c) {
+            return super.getWaitingThreads(c);
+        }
+    }
+
+    /**
+     * Releases write lock, checking that it had a hold count of 1.
+     */
+    void releaseWriteLock(PublicReentrantReadWriteLock lock) {
+        ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
+        assertWriteLockedByMoi(lock);
+        assertEquals(1, lock.getWriteHoldCount());
+        writeLock.unlock();
+        assertNotWriteLocked(lock);
+    }
+
+    /**
+     * Spin-waits until lock.hasQueuedThread(t) becomes true.
+     */
+    void waitForQueuedThread(PublicReentrantReadWriteLock lock, Thread t) {
+        long startTime = System.nanoTime();
+        while (!lock.hasQueuedThread(t)) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                throw new AssertionFailedError("timed out");
+            Thread.yield();
+        }
+        assertTrue(t.isAlive());
+        assertNotSame(t, lock.getOwner());
+    }
+
+    /**
+     * Checks that lock is not write-locked.
+     */
+    void assertNotWriteLocked(PublicReentrantReadWriteLock lock) {
+        assertFalse(lock.isWriteLocked());
+        assertFalse(lock.isWriteLockedByCurrentThread());
+        assertFalse(lock.writeLock().isHeldByCurrentThread());
+        assertEquals(0, lock.getWriteHoldCount());
+        assertEquals(0, lock.writeLock().getHoldCount());
+        assertNull(lock.getOwner());
+    }
+
+    /**
+     * Checks that lock is write-locked by the given thread.
+     */
+    void assertWriteLockedBy(PublicReentrantReadWriteLock lock, Thread t) {
+        assertTrue(lock.isWriteLocked());
+        assertSame(t, lock.getOwner());
+        assertEquals(t == Thread.currentThread(),
+                     lock.isWriteLockedByCurrentThread());
+        assertEquals(t == Thread.currentThread(),
+                     lock.writeLock().isHeldByCurrentThread());
+        assertEquals(t == Thread.currentThread(),
+                     lock.getWriteHoldCount() > 0);
+        assertEquals(t == Thread.currentThread(),
+                     lock.writeLock().getHoldCount() > 0);
+        assertEquals(0, lock.getReadLockCount());
+    }
+
+    /**
+     * Checks that lock is write-locked by the current thread.
+     */
+    void assertWriteLockedByMoi(PublicReentrantReadWriteLock lock) {
+        assertWriteLockedBy(lock, Thread.currentThread());
+    }
+
+    /**
+     * Checks that condition c has no waiters.
+     */
+    void assertHasNoWaiters(PublicReentrantReadWriteLock lock, Condition c) {
+        assertHasWaiters(lock, c, new Thread[] {});
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads.
+     */
+    void assertHasWaiters(PublicReentrantReadWriteLock lock, Condition c,
+                          Thread... threads) {
+        lock.writeLock().lock();
+        assertEquals(threads.length > 0, lock.hasWaiters(c));
+        assertEquals(threads.length, lock.getWaitQueueLength(c));
+        assertEquals(threads.length == 0, lock.getWaitingThreads(c).isEmpty());
+        assertEquals(threads.length, lock.getWaitingThreads(c).size());
+        assertEquals(new HashSet<Thread>(lock.getWaitingThreads(c)),
+                     new HashSet<Thread>(Arrays.asList(threads)));
+        lock.writeLock().unlock();
+    }
+
+    enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil };
+
+    /**
+     * Awaits condition using the specified AwaitMethod.
+     */
+    void await(Condition c, AwaitMethod awaitMethod)
+            throws InterruptedException {
+        switch (awaitMethod) {
+        case await:
+            c.await();
+            break;
+        case awaitTimed:
+            assertTrue(c.await(2 * LONG_DELAY_MS, MILLISECONDS));
+            break;
+        case awaitNanos:
+            long nanosRemaining = c.awaitNanos(MILLISECONDS.toNanos(2 * LONG_DELAY_MS));
+            assertTrue(nanosRemaining > 0);
+            break;
+        case awaitUntil:
+            java.util.Date d = new java.util.Date();
+            assertTrue(c.awaitUntil(new java.util.Date(d.getTime() + 2 * LONG_DELAY_MS)));
+            break;
+        }
+    }
+
+    /**
+     * Constructor sets given fairness, and is in unlocked state
+     */
+    public void testConstructor() {
+        PublicReentrantReadWriteLock lock;
+
+        lock = new PublicReentrantReadWriteLock();
+        assertFalse(lock.isFair());
+        assertNotWriteLocked(lock);
+        assertEquals(0, lock.getReadLockCount());
+
+        lock = new PublicReentrantReadWriteLock(true);
+        assertTrue(lock.isFair());
+        assertNotWriteLocked(lock);
+        assertEquals(0, lock.getReadLockCount());
+
+        lock = new PublicReentrantReadWriteLock(false);
+        assertFalse(lock.isFair());
+        assertNotWriteLocked(lock);
+        assertEquals(0, lock.getReadLockCount());
+    }
+
+    /**
+     * write-locking and read-locking an unlocked lock succeed
+     */
+    public void testLock()      { testLock(false); }
+    public void testLock_fair() { testLock(true); }
+    public void testLock(boolean fair) {
+        PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        assertNotWriteLocked(lock);
+        lock.writeLock().lock();
+        assertWriteLockedByMoi(lock);
+        lock.writeLock().unlock();
+        assertNotWriteLocked(lock);
+        assertEquals(0, lock.getReadLockCount());
+        lock.readLock().lock();
+        assertNotWriteLocked(lock);
+        assertEquals(1, lock.getReadLockCount());
+        lock.readLock().unlock();
+        assertNotWriteLocked(lock);
+        assertEquals(0, lock.getReadLockCount());
+    }
+
+    /**
+     * getWriteHoldCount returns number of recursive holds
+     */
+    public void testGetWriteHoldCount()      { testGetWriteHoldCount(false); }
+    public void testGetWriteHoldCount_fair() { testGetWriteHoldCount(true); }
+    public void testGetWriteHoldCount(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        for (int i = 1; i <= SIZE; i++) {
+            lock.writeLock().lock();
+            assertEquals(i,lock.getWriteHoldCount());
+        }
+        for (int i = SIZE; i > 0; i--) {
+            lock.writeLock().unlock();
+            assertEquals(i-1,lock.getWriteHoldCount());
+        }
+    }
+
+    /**
+     * writelock.getHoldCount returns number of recursive holds
+     */
+    public void testGetHoldCount()      { testGetHoldCount(false); }
+    public void testGetHoldCount_fair() { testGetHoldCount(true); }
+    public void testGetHoldCount(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        for (int i = 1; i <= SIZE; i++) {
+            lock.writeLock().lock();
+            assertEquals(i,lock.writeLock().getHoldCount());
+        }
+        for (int i = SIZE; i > 0; i--) {
+            lock.writeLock().unlock();
+            assertEquals(i-1,lock.writeLock().getHoldCount());
+        }
+    }
+
+    /**
+     * getReadHoldCount returns number of recursive holds
+     */
+    public void testGetReadHoldCount()      { testGetReadHoldCount(false); }
+    public void testGetReadHoldCount_fair() { testGetReadHoldCount(true); }
+    public void testGetReadHoldCount(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        for (int i = 1; i <= SIZE; i++) {
+            lock.readLock().lock();
+            assertEquals(i,lock.getReadHoldCount());
+        }
+        for (int i = SIZE; i > 0; i--) {
+            lock.readLock().unlock();
+            assertEquals(i-1,lock.getReadHoldCount());
+        }
+    }
+
+    /**
+     * write-unlocking an unlocked lock throws IllegalMonitorStateException
+     */
+    public void testWriteUnlock_IMSE()      { testWriteUnlock_IMSE(false); }
+    public void testWriteUnlock_IMSE_fair() { testWriteUnlock_IMSE(true); }
+    public void testWriteUnlock_IMSE(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        try {
+            lock.writeLock().unlock();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * read-unlocking an unlocked lock throws IllegalMonitorStateException
+     */
+    public void testReadUnlock_IMSE()      { testReadUnlock_IMSE(false); }
+    public void testReadUnlock_IMSE_fair() { testReadUnlock_IMSE(true); }
+    public void testReadUnlock_IMSE(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        try {
+            lock.readLock().unlock();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * write-lockInterruptibly is interruptible
+     */
+    public void testWriteLockInterruptibly_Interruptible()      { testWriteLockInterruptibly_Interruptible(false); }
+    public void testWriteLockInterruptibly_Interruptible_fair() { testWriteLockInterruptibly_Interruptible(true); }
+    public void testWriteLockInterruptibly_Interruptible(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lockInterruptibly();
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * timed write-tryLock is interruptible
+     */
+    public void testWriteTryLock_Interruptible()      { testWriteTryLock_Interruptible(false); }
+    public void testWriteTryLock_Interruptible_fair() { testWriteTryLock_Interruptible(true); }
+    public void testWriteTryLock_Interruptible(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().tryLock(2 * LONG_DELAY_MS, MILLISECONDS);
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * read-lockInterruptibly is interruptible
+     */
+    public void testReadLockInterruptibly_Interruptible()      { testReadLockInterruptibly_Interruptible(false); }
+    public void testReadLockInterruptibly_Interruptible_fair() { testReadLockInterruptibly_Interruptible(true); }
+    public void testReadLockInterruptibly_Interruptible(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.readLock().lockInterruptibly();
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * timed read-tryLock is interruptible
+     */
+    public void testReadTryLock_Interruptible()      { testReadTryLock_Interruptible(false); }
+    public void testReadTryLock_Interruptible_fair() { testReadTryLock_Interruptible(true); }
+    public void testReadTryLock_Interruptible(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.readLock().tryLock(2 * LONG_DELAY_MS, MILLISECONDS);
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * write-tryLock on an unlocked lock succeeds
+     */
+    public void testWriteTryLock()      { testWriteTryLock(false); }
+    public void testWriteTryLock_fair() { testWriteTryLock(true); }
+    public void testWriteTryLock(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        assertTrue(lock.writeLock().tryLock());
+        assertWriteLockedByMoi(lock);
+        assertTrue(lock.writeLock().tryLock());
+        assertWriteLockedByMoi(lock);
+        lock.writeLock().unlock();
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * write-tryLock fails if locked
+     */
+    public void testWriteTryLockWhenLocked()      { testWriteTryLockWhenLocked(false); }
+    public void testWriteTryLockWhenLocked_fair() { testWriteTryLockWhenLocked(true); }
+    public void testWriteTryLockWhenLocked(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(lock.writeLock().tryLock());
+            }});
+
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * read-tryLock fails if locked
+     */
+    public void testReadTryLockWhenLocked()      { testReadTryLockWhenLocked(false); }
+    public void testReadTryLockWhenLocked_fair() { testReadTryLockWhenLocked(true); }
+    public void testReadTryLockWhenLocked(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(lock.readLock().tryLock());
+            }});
+
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * Multiple threads can hold a read lock when not write-locked
+     */
+    public void testMultipleReadLocks()      { testMultipleReadLocks(false); }
+    public void testMultipleReadLocks_fair() { testMultipleReadLocks(true); }
+    public void testMultipleReadLocks(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertTrue(lock.readLock().tryLock());
+                lock.readLock().unlock();
+                assertTrue(lock.readLock().tryLock(LONG_DELAY_MS, MILLISECONDS));
+                lock.readLock().unlock();
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+
+        awaitTermination(t);
+        lock.readLock().unlock();
+    }
+
+    /**
+     * A writelock succeeds only after a reading thread unlocks
+     */
+    public void testWriteAfterReadLock()      { testWriteAfterReadLock(false); }
+    public void testWriteAfterReadLock_fair() { testWriteAfterReadLock(true); }
+    public void testWriteAfterReadLock(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(1, lock.getReadLockCount());
+                lock.writeLock().lock();
+                assertEquals(0, lock.getReadLockCount());
+                lock.writeLock().unlock();
+            }});
+        waitForQueuedThread(lock, t);
+        assertNotWriteLocked(lock);
+        assertEquals(1, lock.getReadLockCount());
+        lock.readLock().unlock();
+        assertEquals(0, lock.getReadLockCount());
+        awaitTermination(t);
+        assertNotWriteLocked(lock);
+    }
+
+    /**
+     * A writelock succeeds only after reading threads unlock
+     */
+    public void testWriteAfterMultipleReadLocks()      { testWriteAfterMultipleReadLocks(false); }
+    public void testWriteAfterMultipleReadLocks_fair() { testWriteAfterMultipleReadLocks(true); }
+    public void testWriteAfterMultipleReadLocks(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+        lock.readLock().lock();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                assertEquals(3, lock.getReadLockCount());
+                lock.readLock().unlock();
+            }});
+        awaitTermination(t1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(2, lock.getReadLockCount());
+                lock.writeLock().lock();
+                assertEquals(0, lock.getReadLockCount());
+                lock.writeLock().unlock();
+            }});
+        waitForQueuedThread(lock, t2);
+        assertNotWriteLocked(lock);
+        assertEquals(2, lock.getReadLockCount());
+        lock.readLock().unlock();
+        lock.readLock().unlock();
+        assertEquals(0, lock.getReadLockCount());
+        awaitTermination(t2);
+        assertNotWriteLocked(lock);
+    }
+
+    /**
+     * A thread that tries to acquire a fair read lock (non-reentrantly)
+     * will block if there is a waiting writer thread
+     */
+    public void testReaderWriterReaderFairFifo() {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(true);
+        final AtomicBoolean t1GotLock = new AtomicBoolean(false);
+
+        lock.readLock().lock();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(1, lock.getReadLockCount());
+                lock.writeLock().lock();
+                assertEquals(0, lock.getReadLockCount());
+                t1GotLock.set(true);
+                lock.writeLock().unlock();
+            }});
+        waitForQueuedThread(lock, t1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(1, lock.getReadLockCount());
+                lock.readLock().lock();
+                assertEquals(1, lock.getReadLockCount());
+                assertTrue(t1GotLock.get());
+                lock.readLock().unlock();
+            }});
+        waitForQueuedThread(lock, t2);
+        assertTrue(t1.isAlive());
+        assertNotWriteLocked(lock);
+        assertEquals(1, lock.getReadLockCount());
+        lock.readLock().unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertNotWriteLocked(lock);
+    }
+
+    /**
+     * Readlocks succeed only after a writing thread unlocks
+     */
+    public void testReadAfterWriteLock()      { testReadAfterWriteLock(false); }
+    public void testReadAfterWriteLock_fair() { testReadAfterWriteLock(true); }
+    public void testReadAfterWriteLock(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+
+        waitForQueuedThread(lock, t1);
+        waitForQueuedThread(lock, t2);
+        releaseWriteLock(lock);
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Read trylock succeeds if write locked by current thread
+     */
+    public void testReadHoldingWriteLock()      { testReadHoldingWriteLock(false); }
+    public void testReadHoldingWriteLock_fair() { testReadHoldingWriteLock(true); }
+    public void testReadHoldingWriteLock(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        assertTrue(lock.readLock().tryLock());
+        lock.readLock().unlock();
+        lock.writeLock().unlock();
+    }
+
+    /**
+     * Read trylock succeeds (barging) even in the presence of waiting
+     * readers and/or writers
+     */
+    public void testReadTryLockBarging()      { testReadTryLockBarging(false); }
+    public void testReadTryLockBarging_fair() { testReadTryLockBarging(true); }
+    public void testReadTryLockBarging(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.writeLock().lock();
+                lock.writeLock().unlock();
+            }});
+
+        waitForQueuedThread(lock, t1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+
+        if (fair)
+            waitForQueuedThread(lock, t2);
+
+        Thread t3 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().tryLock();
+                lock.readLock().unlock();
+            }});
+
+        assertTrue(lock.getReadLockCount() > 0);
+        awaitTermination(t3);
+        assertTrue(t1.isAlive());
+        if (fair) assertTrue(t2.isAlive());
+        lock.readLock().unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Read lock succeeds if write locked by current thread even if
+     * other threads are waiting for readlock
+     */
+    public void testReadHoldingWriteLock2()      { testReadHoldingWriteLock2(false); }
+    public void testReadHoldingWriteLock2_fair() { testReadHoldingWriteLock2(true); }
+    public void testReadHoldingWriteLock2(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        lock.readLock().lock();
+        lock.readLock().unlock();
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+
+        waitForQueuedThread(lock, t1);
+        waitForQueuedThread(lock, t2);
+        assertWriteLockedByMoi(lock);
+        lock.readLock().lock();
+        lock.readLock().unlock();
+        releaseWriteLock(lock);
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Read lock succeeds if write locked by current thread even if
+     * other threads are waiting for writelock
+     */
+    public void testReadHoldingWriteLock3()      { testReadHoldingWriteLock3(false); }
+    public void testReadHoldingWriteLock3_fair() { testReadHoldingWriteLock3(true); }
+    public void testReadHoldingWriteLock3(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        lock.readLock().lock();
+        lock.readLock().unlock();
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.writeLock().lock();
+                lock.writeLock().unlock();
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.writeLock().lock();
+                lock.writeLock().unlock();
+            }});
+
+        waitForQueuedThread(lock, t1);
+        waitForQueuedThread(lock, t2);
+        assertWriteLockedByMoi(lock);
+        lock.readLock().lock();
+        lock.readLock().unlock();
+        assertWriteLockedByMoi(lock);
+        lock.writeLock().unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Write lock succeeds if write locked by current thread even if
+     * other threads are waiting for writelock
+     */
+    public void testWriteHoldingWriteLock4()      { testWriteHoldingWriteLock4(false); }
+    public void testWriteHoldingWriteLock4_fair() { testWriteHoldingWriteLock4(true); }
+    public void testWriteHoldingWriteLock4(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        lock.writeLock().lock();
+        lock.writeLock().unlock();
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.writeLock().lock();
+                lock.writeLock().unlock();
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.writeLock().lock();
+                lock.writeLock().unlock();
+            }});
+
+        waitForQueuedThread(lock, t1);
+        waitForQueuedThread(lock, t2);
+        assertWriteLockedByMoi(lock);
+        assertEquals(1, lock.getWriteHoldCount());
+        lock.writeLock().lock();
+        assertWriteLockedByMoi(lock);
+        assertEquals(2, lock.getWriteHoldCount());
+        lock.writeLock().unlock();
+        assertWriteLockedByMoi(lock);
+        assertEquals(1, lock.getWriteHoldCount());
+        lock.writeLock().unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Read tryLock succeeds if readlocked but not writelocked
+     */
+    public void testTryLockWhenReadLocked()      { testTryLockWhenReadLocked(false); }
+    public void testTryLockWhenReadLocked_fair() { testTryLockWhenReadLocked(true); }
+    public void testTryLockWhenReadLocked(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertTrue(lock.readLock().tryLock());
+                lock.readLock().unlock();
+            }});
+
+        awaitTermination(t);
+        lock.readLock().unlock();
+    }
+
+    /**
+     * write tryLock fails when readlocked
+     */
+    public void testWriteTryLockWhenReadLocked()      { testWriteTryLockWhenReadLocked(false); }
+    public void testWriteTryLockWhenReadLocked_fair() { testWriteTryLockWhenReadLocked(true); }
+    public void testWriteTryLockWhenReadLocked(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(lock.writeLock().tryLock());
+            }});
+
+        awaitTermination(t);
+        lock.readLock().unlock();
+    }
+
+    /**
+     * write timed tryLock times out if locked
+     */
+    public void testWriteTryLock_Timeout()      { testWriteTryLock_Timeout(false); }
+    public void testWriteTryLock_Timeout_fair() { testWriteTryLock_Timeout(true); }
+    public void testWriteTryLock_Timeout(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                long timeoutMillis = 10;
+                assertFalse(lock.writeLock().tryLock(timeoutMillis, MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            }});
+
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * read timed tryLock times out if write-locked
+     */
+    public void testReadTryLock_Timeout()      { testReadTryLock_Timeout(false); }
+    public void testReadTryLock_Timeout_fair() { testReadTryLock_Timeout(true); }
+    public void testReadTryLock_Timeout(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                long timeoutMillis = 10;
+                assertFalse(lock.readLock().tryLock(timeoutMillis, MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            }});
+
+        awaitTermination(t);
+        assertTrue(lock.writeLock().isHeldByCurrentThread());
+        lock.writeLock().unlock();
+    }
+
+    /**
+     * write lockInterruptibly succeeds if unlocked, else is interruptible
+     */
+    public void testWriteLockInterruptibly()      { testWriteLockInterruptibly(false); }
+    public void testWriteLockInterruptibly_fair() { testWriteLockInterruptibly(true); }
+    public void testWriteLockInterruptibly(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        try {
+            lock.writeLock().lockInterruptibly();
+        } catch (InterruptedException ie) {
+            threadUnexpectedException(ie);
+        }
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lockInterruptibly();
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        assertTrue(lock.writeLock().isHeldByCurrentThread());
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * read lockInterruptibly succeeds if lock free else is interruptible
+     */
+    public void testReadLockInterruptibly()      { testReadLockInterruptibly(false); }
+    public void testReadLockInterruptibly_fair() { testReadLockInterruptibly(true); }
+    public void testReadLockInterruptibly(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        try {
+            lock.readLock().lockInterruptibly();
+            lock.readLock().unlock();
+            lock.writeLock().lockInterruptibly();
+        } catch (InterruptedException ie) {
+            threadUnexpectedException(ie);
+        }
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.readLock().lockInterruptibly();
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * Calling await without holding lock throws IllegalMonitorStateException
+     */
+    public void testAwait_IMSE()      { testAwait_IMSE(false); }
+    public void testAwait_IMSE_fair() { testAwait_IMSE(true); }
+    public void testAwait_IMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        for (AwaitMethod awaitMethod : AwaitMethod.values()) {
+            long startTime = System.nanoTime();
+            try {
+                await(c, awaitMethod);
+                shouldThrow();
+            } catch (IllegalMonitorStateException success) {
+            } catch (InterruptedException e) { threadUnexpectedException(e); }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * Calling signal without holding lock throws IllegalMonitorStateException
+     */
+    public void testSignal_IMSE()      { testSignal_IMSE(false); }
+    public void testSignal_IMSE_fair() { testSignal_IMSE(true); }
+    public void testSignal_IMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        try {
+            c.signal();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * Calling signalAll without holding lock throws IllegalMonitorStateException
+     */
+    public void testSignalAll_IMSE()      { testSignalAll_IMSE(false); }
+    public void testSignalAll_IMSE_fair() { testSignalAll_IMSE(true); }
+    public void testSignalAll_IMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        try {
+            c.signalAll();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * awaitNanos without a signal times out
+     */
+    public void testAwaitNanos_Timeout()      { testAwaitNanos_Timeout(false); }
+    public void testAwaitNanos_Timeout_fair() { testAwaitNanos_Timeout(true); }
+    public void testAwaitNanos_Timeout(boolean fair) {
+        try {
+            final ReentrantReadWriteLock lock =
+                new ReentrantReadWriteLock(fair);
+            final Condition c = lock.writeLock().newCondition();
+            lock.writeLock().lock();
+            long startTime = System.nanoTime();
+            long timeoutMillis = 10;
+            long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis);
+            long nanosRemaining = c.awaitNanos(timeoutNanos);
+            assertTrue(nanosRemaining <= 0);
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            lock.writeLock().unlock();
+        } catch (InterruptedException e) {
+            threadUnexpectedException(e);
+        }
+    }
+
+    /**
+     * timed await without a signal times out
+     */
+    public void testAwait_Timeout()      { testAwait_Timeout(false); }
+    public void testAwait_Timeout_fair() { testAwait_Timeout(true); }
+    public void testAwait_Timeout(boolean fair) {
+        try {
+            final ReentrantReadWriteLock lock =
+                new ReentrantReadWriteLock(fair);
+            final Condition c = lock.writeLock().newCondition();
+            lock.writeLock().lock();
+            long startTime = System.nanoTime();
+            long timeoutMillis = 10;
+            assertFalse(c.await(timeoutMillis, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            lock.writeLock().unlock();
+        } catch (InterruptedException e) {
+            threadUnexpectedException(e);
+        }
+    }
+
+    /**
+     * awaitUntil without a signal times out
+     */
+    public void testAwaitUntil_Timeout()      { testAwaitUntil_Timeout(false); }
+    public void testAwaitUntil_Timeout_fair() { testAwaitUntil_Timeout(true); }
+    public void testAwaitUntil_Timeout(boolean fair) {
+        try {
+            final ReentrantReadWriteLock lock =
+                new ReentrantReadWriteLock(fair);
+            final Condition c = lock.writeLock().newCondition();
+            lock.writeLock().lock();
+            long startTime = System.nanoTime();
+            long timeoutMillis = 10;
+            java.util.Date d = new java.util.Date();
+            assertFalse(c.awaitUntil(new java.util.Date(d.getTime() + timeoutMillis)));
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            lock.writeLock().unlock();
+        } catch (InterruptedException e) {
+            threadUnexpectedException(e);
+        }
+    }
+
+    /**
+     * await returns when signalled
+     */
+    public void testAwait()      { testAwait(false); }
+    public void testAwait_fair() { testAwait(true); }
+    public void testAwait(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                locked.countDown();
+                c.await();
+                lock.writeLock().unlock();
+            }});
+
+        await(locked);
+        lock.writeLock().lock();
+        assertHasWaiters(lock, c, t);
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertTrue(t.isAlive());
+        lock.writeLock().unlock();
+        awaitTermination(t);
+    }
+
+    /**
+     * awaitUninterruptibly is uninterruptible
+     */
+    public void testAwaitUninterruptibly()      { testAwaitUninterruptibly(false); }
+    public void testAwaitUninterruptibly_fair() { testAwaitUninterruptibly(true); }
+    public void testAwaitUninterruptibly(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(2);
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                // Interrupt before awaitUninterruptibly
+                lock.writeLock().lock();
+                pleaseInterrupt.countDown();
+                Thread.currentThread().interrupt();
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                lock.writeLock().unlock();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                // Interrupt during awaitUninterruptibly
+                lock.writeLock().lock();
+                pleaseInterrupt.countDown();
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                lock.writeLock().unlock();
+            }});
+
+        await(pleaseInterrupt);
+        lock.writeLock().lock();
+        lock.writeLock().unlock();
+        t2.interrupt();
+
+        assertThreadStaysAlive(t1);
+        assertTrue(t2.isAlive());
+
+        lock.writeLock().lock();
+        c.signalAll();
+        lock.writeLock().unlock();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil is interruptible
+     */
+    public void testInterruptible_await()           { testInterruptible(false, AwaitMethod.await); }
+    public void testInterruptible_await_fair()      { testInterruptible(true,  AwaitMethod.await); }
+    public void testInterruptible_awaitTimed()      { testInterruptible(false, AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitTimed_fair() { testInterruptible(true,  AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitNanos()      { testInterruptible(false, AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitNanos_fair() { testInterruptible(true,  AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitUntil()      { testInterruptible(false, AwaitMethod.awaitUntil); }
+    public void testInterruptible_awaitUntil_fair() { testInterruptible(true,  AwaitMethod.awaitUntil); }
+    public void testInterruptible(boolean fair, final AwaitMethod awaitMethod) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertWriteLockedByMoi(lock);
+                assertHasNoWaiters(lock, c);
+                locked.countDown();
+                try {
+                    await(c, awaitMethod);
+                } finally {
+                    assertWriteLockedByMoi(lock);
+                    assertHasNoWaiters(lock, c);
+                    lock.writeLock().unlock();
+                    assertFalse(Thread.interrupted());
+                }
+            }});
+
+        await(locked);
+        assertHasWaiters(lock, c, t);
+        t.interrupt();
+        awaitTermination(t);
+        assertNotWriteLocked(lock);
+    }
+
+    /**
+     * signalAll wakes up all threads
+     */
+    public void testSignalAll_await()           { testSignalAll(false, AwaitMethod.await); }
+    public void testSignalAll_await_fair()      { testSignalAll(true,  AwaitMethod.await); }
+    public void testSignalAll_awaitTimed()      { testSignalAll(false, AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitTimed_fair() { testSignalAll(true,  AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitNanos()      { testSignalAll(false, AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitNanos_fair() { testSignalAll(true,  AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitUntil()      { testSignalAll(false, AwaitMethod.awaitUntil); }
+    public void testSignalAll_awaitUntil_fair() { testSignalAll(true,  AwaitMethod.awaitUntil); }
+    public void testSignalAll(boolean fair, final AwaitMethod awaitMethod) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(2);
+        final Lock writeLock = lock.writeLock();
+        class Awaiter extends CheckedRunnable {
+            public void realRun() throws InterruptedException {
+                writeLock.lock();
+                locked.countDown();
+                await(c, awaitMethod);
+                writeLock.unlock();
+            }
+        }
+
+        Thread t1 = newStartedThread(new Awaiter());
+        Thread t2 = newStartedThread(new Awaiter());
+
+        await(locked);
+        writeLock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        writeLock.unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * signal wakes up waiting threads in FIFO order
+     */
+    public void testSignalWakesFifo()      { testSignalWakesFifo(false); }
+    public void testSignalWakesFifo_fair() { testSignalWakesFifo(true); }
+    public void testSignalWakesFifo(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked1 = new CountDownLatch(1);
+        final CountDownLatch locked2 = new CountDownLatch(1);
+        final Lock writeLock = lock.writeLock();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                writeLock.lock();
+                locked1.countDown();
+                c.await();
+                writeLock.unlock();
+            }});
+
+        await(locked1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                writeLock.lock();
+                locked2.countDown();
+                c.await();
+                writeLock.unlock();
+            }});
+
+        await(locked2);
+
+        writeLock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        assertFalse(lock.hasQueuedThreads());
+        c.signal();
+        assertHasWaiters(lock, c, t2);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        writeLock.unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * await after multiple reentrant locking preserves lock count
+     */
+    public void testAwaitLockCount()      { testAwaitLockCount(false); }
+    public void testAwaitLockCount_fair() { testAwaitLockCount(true); }
+    public void testAwaitLockCount(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(2);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertWriteLockedByMoi(lock);
+                assertEquals(1, lock.writeLock().getHoldCount());
+                locked.countDown();
+                c.await();
+                assertWriteLockedByMoi(lock);
+                assertEquals(1, lock.writeLock().getHoldCount());
+                lock.writeLock().unlock();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                lock.writeLock().lock();
+                assertWriteLockedByMoi(lock);
+                assertEquals(2, lock.writeLock().getHoldCount());
+                locked.countDown();
+                c.await();
+                assertWriteLockedByMoi(lock);
+                assertEquals(2, lock.writeLock().getHoldCount());
+                lock.writeLock().unlock();
+                lock.writeLock().unlock();
+            }});
+
+        await(locked);
+        lock.writeLock().lock();
+        assertHasWaiters(lock, c, t1, t2);
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.writeLock().unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A serialized lock deserializes as unlocked
+     */
+    public void testSerialization()      { testSerialization(false); }
+    public void testSerialization_fair() { testSerialization(true); }
+    public void testSerialization(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        lock.readLock().lock();
+
+        ReentrantReadWriteLock clone = serialClone(lock);
+        assertEquals(lock.isFair(), clone.isFair());
+        assertTrue(lock.isWriteLocked());
+        assertFalse(clone.isWriteLocked());
+        assertEquals(1, lock.getReadLockCount());
+        assertEquals(0, clone.getReadLockCount());
+        clone.writeLock().lock();
+        clone.readLock().lock();
+        assertTrue(clone.isWriteLocked());
+        assertEquals(1, clone.getReadLockCount());
+        clone.readLock().unlock();
+        clone.writeLock().unlock();
+        assertFalse(clone.isWriteLocked());
+        assertEquals(1, lock.getReadLockCount());
+        assertEquals(0, clone.getReadLockCount());
+    }
+
+    /**
+     * hasQueuedThreads reports whether there are waiting threads
+     */
+    public void testHasQueuedThreads()      { testHasQueuedThreads(false); }
+    public void testHasQueuedThreads_fair() { testHasQueuedThreads(true); }
+    public void testHasQueuedThreads(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertFalse(lock.hasQueuedThreads());
+        lock.writeLock().lock();
+        assertFalse(lock.hasQueuedThreads());
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertTrue(lock.hasQueuedThreads());
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertTrue(lock.hasQueuedThreads());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(lock.hasQueuedThreads());
+        lock.writeLock().unlock();
+        awaitTermination(t2);
+        assertFalse(lock.hasQueuedThreads());
+    }
+
+    /**
+     * hasQueuedThread(null) throws NPE
+     */
+    public void testHasQueuedThreadNPE()      { testHasQueuedThreadNPE(false); }
+    public void testHasQueuedThreadNPE_fair() { testHasQueuedThreadNPE(true); }
+    public void testHasQueuedThreadNPE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        try {
+            lock.hasQueuedThread(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasQueuedThread reports whether a thread is queued
+     */
+    public void testHasQueuedThread()      { testHasQueuedThread(false); }
+    public void testHasQueuedThread_fair() { testHasQueuedThread(true); }
+    public void testHasQueuedThread(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertFalse(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        lock.writeLock().lock();
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        lock.writeLock().unlock();
+        awaitTermination(t2);
+        assertFalse(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+    }
+
+    /**
+     * getQueueLength reports number of waiting threads
+     */
+    public void testGetQueueLength()      { testGetQueueLength(false); }
+    public void testGetQueueLength_fair() { testGetQueueLength(true); }
+    public void testGetQueueLength(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertEquals(0, lock.getQueueLength());
+        lock.writeLock().lock();
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertEquals(1, lock.getQueueLength());
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertEquals(2, lock.getQueueLength());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertEquals(1, lock.getQueueLength());
+        lock.writeLock().unlock();
+        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 PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertTrue(lock.getQueuedThreads().isEmpty());
+        lock.writeLock().lock();
+        assertTrue(lock.getQueuedThreads().isEmpty());
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertEquals(1, lock.getQueuedThreads().size());
+        assertTrue(lock.getQueuedThreads().contains(t1));
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertEquals(2, lock.getQueuedThreads().size());
+        assertTrue(lock.getQueuedThreads().contains(t1));
+        assertTrue(lock.getQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(lock.getQueuedThreads().contains(t1));
+        assertTrue(lock.getQueuedThreads().contains(t2));
+        assertEquals(1, lock.getQueuedThreads().size());
+        lock.writeLock().unlock();
+        awaitTermination(t2);
+        assertTrue(lock.getQueuedThreads().isEmpty());
+    }
+
+    /**
+     * hasWaiters throws NPE if null
+     */
+    public void testHasWaitersNPE()      { testHasWaitersNPE(false); }
+    public void testHasWaitersNPE_fair() { testHasWaitersNPE(true); }
+    public void testHasWaitersNPE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        try {
+            lock.hasWaiters(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws NPE if null
+     */
+    public void testGetWaitQueueLengthNPE()      { testGetWaitQueueLengthNPE(false); }
+    public void testGetWaitQueueLengthNPE_fair() { testGetWaitQueueLengthNPE(true); }
+    public void testGetWaitQueueLengthNPE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        try {
+            lock.getWaitQueueLength(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws NPE if null
+     */
+    public void testGetWaitingThreadsNPE()      { testGetWaitingThreadsNPE(false); }
+    public void testGetWaitingThreadsNPE_fair() { testGetWaitingThreadsNPE(true); }
+    public void testGetWaitingThreadsNPE(boolean fair) {
+        final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(fair);
+        try {
+            lock.getWaitingThreads(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalArgumentException if not owned
+     */
+    public void testHasWaitersIAE()      { testHasWaitersIAE(false); }
+    public void testHasWaitersIAE_fair() { testHasWaitersIAE(true); }
+    public void testHasWaitersIAE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final ReentrantReadWriteLock lock2 = new ReentrantReadWriteLock(fair);
+        try {
+            lock2.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalMonitorStateException if not locked
+     */
+    public void testHasWaitersIMSE()      { testHasWaitersIMSE(false); }
+    public void testHasWaitersIMSE_fair() { testHasWaitersIMSE(true); }
+    public void testHasWaitersIMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        try {
+            lock.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitQueueLengthIAE()      { testGetWaitQueueLengthIAE(false); }
+    public void testGetWaitQueueLengthIAE_fair() { testGetWaitQueueLengthIAE(true); }
+    public void testGetWaitQueueLengthIAE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final ReentrantReadWriteLock lock2 = new ReentrantReadWriteLock(fair);
+        try {
+            lock2.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalMonitorStateException if not locked
+     */
+    public void testGetWaitQueueLengthIMSE()      { testGetWaitQueueLengthIMSE(false); }
+    public void testGetWaitQueueLengthIMSE_fair() { testGetWaitQueueLengthIMSE(true); }
+    public void testGetWaitQueueLengthIMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        try {
+            lock.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitingThreadsIAE()      { testGetWaitingThreadsIAE(false); }
+    public void testGetWaitingThreadsIAE_fair() { testGetWaitingThreadsIAE(true); }
+    public void testGetWaitingThreadsIAE(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final PublicReentrantReadWriteLock lock2 =
+            new PublicReentrantReadWriteLock(fair);
+        try {
+            lock2.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws IllegalMonitorStateException if not locked
+     */
+    public void testGetWaitingThreadsIMSE()      { testGetWaitingThreadsIMSE(false); }
+    public void testGetWaitingThreadsIMSE_fair() { testGetWaitingThreadsIMSE(true); }
+    public void testGetWaitingThreadsIMSE(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        try {
+            lock.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * hasWaiters returns true when a thread is waiting, else false
+     */
+    public void testHasWaiters()      { testHasWaiters(false); }
+    public void testHasWaiters_fair() { testHasWaiters(true); }
+    public void testHasWaiters(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertHasNoWaiters(lock, c);
+                assertFalse(lock.hasWaiters(c));
+                locked.countDown();
+                c.await();
+                assertHasNoWaiters(lock, c);
+                assertFalse(lock.hasWaiters(c));
+                lock.writeLock().unlock();
+            }});
+
+        await(locked);
+        lock.writeLock().lock();
+        assertHasWaiters(lock, c, t);
+        assertTrue(lock.hasWaiters(c));
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertFalse(lock.hasWaiters(c));
+        lock.writeLock().unlock();
+        awaitTermination(t);
+        assertHasNoWaiters(lock, c);
+    }
+
+    /**
+     * getWaitQueueLength returns number of waiting threads
+     */
+    public void testGetWaitQueueLength()      { testGetWaitQueueLength(false); }
+    public void testGetWaitQueueLength_fair() { testGetWaitQueueLength(true); }
+    public void testGetWaitQueueLength(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertEquals(0, lock.getWaitQueueLength(c));
+                locked.countDown();
+                c.await();
+                lock.writeLock().unlock();
+            }});
+
+        await(locked);
+        lock.writeLock().lock();
+        assertHasWaiters(lock, c, t);
+        assertEquals(1, lock.getWaitQueueLength(c));
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertEquals(0, lock.getWaitQueueLength(c));
+        lock.writeLock().unlock();
+        awaitTermination(t);
+    }
+
+    /**
+     * getWaitingThreads returns only and all waiting threads
+     */
+    public void testGetWaitingThreads()      { testGetWaitingThreads(false); }
+    public void testGetWaitingThreads_fair() { testGetWaitingThreads(true); }
+    public void testGetWaitingThreads(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked1 = new CountDownLatch(1);
+        final CountDownLatch locked2 = new CountDownLatch(1);
+        Thread t1 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertTrue(lock.getWaitingThreads(c).isEmpty());
+                locked1.countDown();
+                c.await();
+                lock.writeLock().unlock();
+            }});
+
+        Thread t2 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertFalse(lock.getWaitingThreads(c).isEmpty());
+                locked2.countDown();
+                c.await();
+                lock.writeLock().unlock();
+            }});
+
+        lock.writeLock().lock();
+        assertTrue(lock.getWaitingThreads(c).isEmpty());
+        lock.writeLock().unlock();
+
+        t1.start();
+        await(locked1);
+        t2.start();
+        await(locked2);
+
+        lock.writeLock().lock();
+        assertTrue(lock.hasWaiters(c));
+        assertTrue(lock.getWaitingThreads(c).contains(t1));
+        assertTrue(lock.getWaitingThreads(c).contains(t2));
+        assertEquals(2, lock.getWaitingThreads(c).size());
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.writeLock().unlock();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+
+        assertHasNoWaiters(lock, c);
+    }
+
+    /**
+     * toString indicates current lock state
+     */
+    public void testToString()      { testToString(false); }
+    public void testToString_fair() { testToString(true); }
+    public void testToString(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        assertTrue(lock.toString().contains("Write locks = 0"));
+        assertTrue(lock.toString().contains("Read locks = 0"));
+        lock.writeLock().lock();
+        assertTrue(lock.toString().contains("Write locks = 1"));
+        assertTrue(lock.toString().contains("Read locks = 0"));
+        lock.writeLock().unlock();
+        lock.readLock().lock();
+        lock.readLock().lock();
+        assertTrue(lock.toString().contains("Write locks = 0"));
+        assertTrue(lock.toString().contains("Read locks = 2"));
+    }
+
+    /**
+     * readLock.toString indicates current lock state
+     */
+    public void testReadLockToString()      { testReadLockToString(false); }
+    public void testReadLockToString_fair() { testReadLockToString(true); }
+    public void testReadLockToString(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        assertTrue(lock.readLock().toString().contains("Read locks = 0"));
+        lock.readLock().lock();
+        lock.readLock().lock();
+        assertTrue(lock.readLock().toString().contains("Read locks = 2"));
+    }
+
+    /**
+     * writeLock.toString indicates current lock state
+     */
+    public void testWriteLockToString()      { testWriteLockToString(false); }
+    public void testWriteLockToString_fair() { testWriteLockToString(true); }
+    public void testWriteLockToString(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        assertTrue(lock.writeLock().toString().contains("Unlocked"));
+        lock.writeLock().lock();
+        assertTrue(lock.writeLock().toString().contains("Locked"));
+        lock.writeLock().unlock();
+        assertTrue(lock.writeLock().toString().contains("Unlocked"));
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ScheduledExecutorSubclassTest.java b/jsr166-tests/src/test/java/jsr166/ScheduledExecutorSubclassTest.java
new file mode 100644
index 0000000..b72ad02
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ScheduledExecutorSubclassTest.java
@@ -0,0 +1,1213 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+import java.util.concurrent.*;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ScheduledExecutorSubclassTest extends JSR166TestCase {
+
+    static class CustomTask<V> implements RunnableScheduledFuture<V> {
+        RunnableScheduledFuture<V> task;
+        volatile boolean ran;
+        CustomTask(RunnableScheduledFuture<V> t) { task = t; }
+        public boolean isPeriodic() { return task.isPeriodic(); }
+        public void run() {
+            ran = true;
+            task.run();
+        }
+        public long getDelay(TimeUnit unit) { return task.getDelay(unit); }
+        public int compareTo(Delayed t) {
+            return task.compareTo(((CustomTask)t).task);
+        }
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            return task.cancel(mayInterruptIfRunning);
+        }
+        public boolean isCancelled() { return task.isCancelled(); }
+        public boolean isDone() { return task.isDone(); }
+        public V get() throws InterruptedException, ExecutionException {
+            V v = task.get();
+            assertTrue(ran);
+            return v;
+        }
+        public V get(long time, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+            V v = task.get(time, unit);
+            assertTrue(ran);
+            return v;
+        }
+    }
+
+    public class CustomExecutor extends ScheduledThreadPoolExecutor {
+
+        protected <V> RunnableScheduledFuture<V> decorateTask(Runnable r, RunnableScheduledFuture<V> task) {
+            return new CustomTask<V>(task);
+        }
+
+        protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> c, RunnableScheduledFuture<V> task) {
+            return new CustomTask<V>(task);
+        }
+        CustomExecutor(int corePoolSize) { super(corePoolSize); }
+        CustomExecutor(int corePoolSize, RejectedExecutionHandler handler) {
+            super(corePoolSize, handler);
+        }
+
+        CustomExecutor(int corePoolSize, ThreadFactory threadFactory) {
+            super(corePoolSize, threadFactory);
+        }
+        CustomExecutor(int corePoolSize, ThreadFactory threadFactory,
+                       RejectedExecutionHandler handler) {
+            super(corePoolSize, threadFactory, handler);
+        }
+
+    }
+
+    /**
+     * execute successfully executes a runnable
+     */
+    public void testExecute() throws InterruptedException {
+        CustomExecutor p = new CustomExecutor(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        final Runnable task = new CheckedRunnable() {
+            public void realRun() {
+                done.countDown();
+            }};
+        try {
+            p.execute(task);
+            assertTrue(done.await(SMALL_DELAY_MS, MILLISECONDS));
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * delayed schedule of callable successfully executes after delay
+     */
+    public void testSchedule1() throws Exception {
+        CustomExecutor p = new CustomExecutor(1);
+        final long startTime = System.nanoTime();
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            Callable task = new CheckedCallable<Boolean>() {
+                public Boolean realCall() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                    return Boolean.TRUE;
+                }};
+            Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
+            assertSame(Boolean.TRUE, f.get());
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            assertTrue(done.await(0L, MILLISECONDS));
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * delayed schedule of runnable successfully executes after delay
+     */
+    public void testSchedule3() throws Exception {
+        CustomExecutor p = new CustomExecutor(1);
+        final long startTime = System.nanoTime();
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
+            await(done);
+            assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * scheduleAtFixedRate executes runnable after given initial delay
+     */
+    public void testSchedule4() throws InterruptedException {
+        CustomExecutor p = new CustomExecutor(1);
+        final long startTime = System.nanoTime();
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            ScheduledFuture f =
+                p.scheduleAtFixedRate(task, timeoutMillis(),
+                                      LONG_DELAY_MS, MILLISECONDS);
+            await(done);
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            f.cancel(true);
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * scheduleWithFixedDelay executes runnable after given initial delay
+     */
+    public void testSchedule5() throws InterruptedException {
+        CustomExecutor p = new CustomExecutor(1);
+        final long startTime = System.nanoTime();
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            ScheduledFuture f =
+                p.scheduleWithFixedDelay(task, timeoutMillis(),
+                                         LONG_DELAY_MS, MILLISECONDS);
+            await(done);
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            f.cancel(true);
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    static class RunnableCounter implements Runnable {
+        AtomicInteger count = new AtomicInteger(0);
+        public void run() { count.getAndIncrement(); }
+    }
+
+    /**
+     * scheduleAtFixedRate executes series of tasks at given rate
+     */
+    public void testFixedRateSequence() throws InterruptedException {
+        CustomExecutor p = new CustomExecutor(1);
+        RunnableCounter counter = new RunnableCounter();
+        ScheduledFuture h =
+            p.scheduleAtFixedRate(counter, 0, 1, MILLISECONDS);
+        delay(SMALL_DELAY_MS);
+        h.cancel(true);
+        int c = counter.count.get();
+        // By time scaling conventions, we must have at least
+        // an execution per SHORT delay, but no more than one SHORT more
+        assertTrue(c >= SMALL_DELAY_MS / SHORT_DELAY_MS);
+        assertTrue(c <= SMALL_DELAY_MS + SHORT_DELAY_MS);
+        joinPool(p);
+    }
+
+    /**
+     * scheduleWithFixedDelay executes series of tasks with given period
+     */
+    public void testFixedDelaySequence() throws InterruptedException {
+        CustomExecutor p = new CustomExecutor(1);
+        RunnableCounter counter = new RunnableCounter();
+        ScheduledFuture h =
+            p.scheduleWithFixedDelay(counter, 0, 1, MILLISECONDS);
+        delay(SMALL_DELAY_MS);
+        h.cancel(true);
+        int c = counter.count.get();
+        assertTrue(c >= SMALL_DELAY_MS / SHORT_DELAY_MS);
+        assertTrue(c <= SMALL_DELAY_MS + SHORT_DELAY_MS);
+        joinPool(p);
+    }
+
+    /**
+     * execute(null) throws NPE
+     */
+    public void testExecuteNull() throws InterruptedException {
+        CustomExecutor se = new CustomExecutor(1);
+        try {
+            se.execute(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+        joinPool(se);
+    }
+
+    /**
+     * schedule(null) throws NPE
+     */
+    public void testScheduleNull() throws InterruptedException {
+        CustomExecutor se = new CustomExecutor(1);
+        try {
+            TrackedCallable callable = null;
+            Future f = se.schedule(callable, SHORT_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+        joinPool(se);
+    }
+
+    /**
+     * execute throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule1_RejectedExecutionException() {
+        CustomExecutor se = new CustomExecutor(1);
+        try {
+            se.shutdown();
+            se.schedule(new NoOpRunnable(),
+                        MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (RejectedExecutionException success) {
+        } catch (SecurityException ok) {
+        }
+
+        joinPool(se);
+    }
+
+    /**
+     * schedule throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule2_RejectedExecutionException() {
+        CustomExecutor se = new CustomExecutor(1);
+        try {
+            se.shutdown();
+            se.schedule(new NoOpCallable(),
+                        MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (RejectedExecutionException success) {
+        } catch (SecurityException ok) {
+        }
+        joinPool(se);
+    }
+
+    /**
+     * schedule callable throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule3_RejectedExecutionException() {
+        CustomExecutor se = new CustomExecutor(1);
+        try {
+            se.shutdown();
+            se.schedule(new NoOpCallable(),
+                        MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (RejectedExecutionException success) {
+        } catch (SecurityException ok) {
+        }
+        joinPool(se);
+    }
+
+    /**
+     * scheduleAtFixedRate throws RejectedExecutionException if shutdown
+     */
+    public void testScheduleAtFixedRate1_RejectedExecutionException() {
+        CustomExecutor se = new CustomExecutor(1);
+        try {
+            se.shutdown();
+            se.scheduleAtFixedRate(new NoOpRunnable(),
+                                   MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (RejectedExecutionException success) {
+        } catch (SecurityException ok) {
+        }
+        joinPool(se);
+    }
+
+    /**
+     * scheduleWithFixedDelay throws RejectedExecutionException if shutdown
+     */
+    public void testScheduleWithFixedDelay1_RejectedExecutionException() {
+        CustomExecutor se = new CustomExecutor(1);
+        try {
+            se.shutdown();
+            se.scheduleWithFixedDelay(new NoOpRunnable(),
+                                      MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (RejectedExecutionException success) {
+        } catch (SecurityException ok) {
+        }
+        joinPool(se);
+    }
+
+    /**
+     * getActiveCount increases but doesn't overestimate, when a
+     * thread becomes active
+     */
+    public void testGetActiveCount() throws InterruptedException {
+        final ThreadPoolExecutor p = new CustomExecutor(2);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getActiveCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getActiveCount());
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(1, p.getActiveCount());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getCompletedTaskCount increases, but doesn't overestimate,
+     * when tasks complete
+     */
+    public void testGetCompletedTaskCount() throws InterruptedException {
+        final ThreadPoolExecutor p = new CustomExecutor(2);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch threadProceed = new CountDownLatch(1);
+        final CountDownLatch threadDone = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(0, p.getCompletedTaskCount());
+                    threadProceed.await();
+                    threadDone.countDown();
+                }});
+            await(threadStarted);
+            assertEquals(0, p.getCompletedTaskCount());
+            threadProceed.countDown();
+            threadDone.await();
+            long startTime = System.nanoTime();
+            while (p.getCompletedTaskCount() != 1) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    fail("timed out");
+                Thread.yield();
+            }
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getCorePoolSize returns size given in constructor if not otherwise set
+     */
+    public void testGetCorePoolSize() {
+        CustomExecutor p = new CustomExecutor(1);
+        assertEquals(1, p.getCorePoolSize());
+        joinPool(p);
+    }
+
+    /**
+     * getLargestPoolSize increases, but doesn't overestimate, when
+     * multiple threads active
+     */
+    public void testGetLargestPoolSize() throws InterruptedException {
+        final int THREADS = 3;
+        final ThreadPoolExecutor p = new CustomExecutor(THREADS);
+        final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getLargestPoolSize());
+            for (int i = 0; i < THREADS; i++)
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadsStarted.countDown();
+                        done.await();
+                        assertEquals(THREADS, p.getLargestPoolSize());
+                    }});
+            assertTrue(threadsStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(THREADS, p.getLargestPoolSize());
+        } finally {
+            done.countDown();
+            joinPool(p);
+            assertEquals(THREADS, p.getLargestPoolSize());
+        }
+    }
+
+    /**
+     * getPoolSize increases, but doesn't overestimate, when threads
+     * become active
+     */
+    public void testGetPoolSize() throws InterruptedException {
+        final ThreadPoolExecutor p = new CustomExecutor(1);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getPoolSize());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(1, p.getPoolSize());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getTaskCount increases, but doesn't overestimate, when tasks
+     * submitted
+     */
+    public void testGetTaskCount() throws InterruptedException {
+        final ThreadPoolExecutor p = new CustomExecutor(1);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        final int TASKS = 5;
+        try {
+            assertEquals(0, p.getTaskCount());
+            for (int i = 0; i < TASKS; i++)
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        done.await();
+                    }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(TASKS, p.getTaskCount());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getThreadFactory returns factory in constructor if not set
+     */
+    public void testGetThreadFactory() {
+        ThreadFactory tf = new SimpleThreadFactory();
+        CustomExecutor p = new CustomExecutor(1, tf);
+        assertSame(tf, p.getThreadFactory());
+        joinPool(p);
+    }
+
+    /**
+     * setThreadFactory sets the thread factory returned by getThreadFactory
+     */
+    public void testSetThreadFactory() {
+        ThreadFactory tf = new SimpleThreadFactory();
+        CustomExecutor p = new CustomExecutor(1);
+        p.setThreadFactory(tf);
+        assertSame(tf, p.getThreadFactory());
+        joinPool(p);
+    }
+
+    /**
+     * setThreadFactory(null) throws NPE
+     */
+    public void testSetThreadFactoryNull() {
+        CustomExecutor p = new CustomExecutor(1);
+        try {
+            p.setThreadFactory(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * isShutdown is false before shutdown, true after
+     */
+    public void testIsShutdown() {
+        CustomExecutor p = new CustomExecutor(1);
+        try {
+            assertFalse(p.isShutdown());
+        }
+        finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        assertTrue(p.isShutdown());
+    }
+
+    /**
+     * isTerminated is false before termination, true after
+     */
+    public void testIsTerminated() throws InterruptedException {
+        final ThreadPoolExecutor p = new CustomExecutor(1);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        assertFalse(p.isTerminated());
+        try {
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminated());
+                    threadStarted.countDown();
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertFalse(p.isTerminating());
+            done.countDown();
+        } finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+    }
+
+    /**
+     * isTerminating is not true when running or when terminated
+     */
+    public void testIsTerminating() throws InterruptedException {
+        final ThreadPoolExecutor p = new CustomExecutor(1);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertFalse(p.isTerminating());
+            done.countDown();
+        } finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertFalse(p.isTerminating());
+    }
+
+    /**
+     * getQueue returns the work queue, which contains queued tasks
+     */
+    public void testGetQueue() throws InterruptedException {
+        ScheduledThreadPoolExecutor p = new CustomExecutor(1);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            ScheduledFuture[] tasks = new ScheduledFuture[5];
+            for (int i = 0; i < tasks.length; i++) {
+                Runnable r = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        done.await();
+                    }};
+                tasks[i] = p.schedule(r, 1, MILLISECONDS);
+            }
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            BlockingQueue<Runnable> q = p.getQueue();
+            assertTrue(q.contains(tasks[tasks.length - 1]));
+            assertFalse(q.contains(tasks[0]));
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * remove(task) removes queued task, and fails to remove active task
+     */
+    public void testRemove() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new CustomExecutor(1);
+        ScheduledFuture[] tasks = new ScheduledFuture[5];
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            for (int i = 0; i < tasks.length; i++) {
+                Runnable r = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        done.await();
+                    }};
+                tasks[i] = p.schedule(r, 1, MILLISECONDS);
+            }
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            BlockingQueue<Runnable> q = p.getQueue();
+            assertFalse(p.remove((Runnable)tasks[0]));
+            assertTrue(q.contains((Runnable)tasks[4]));
+            assertTrue(q.contains((Runnable)tasks[3]));
+            assertTrue(p.remove((Runnable)tasks[4]));
+            assertFalse(p.remove((Runnable)tasks[4]));
+            assertFalse(q.contains((Runnable)tasks[4]));
+            assertTrue(q.contains((Runnable)tasks[3]));
+            assertTrue(p.remove((Runnable)tasks[3]));
+            assertFalse(q.contains((Runnable)tasks[3]));
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * purge removes cancelled tasks from the queue
+     */
+    public void testPurge() throws InterruptedException {
+        CustomExecutor p = new CustomExecutor(1);
+        ScheduledFuture[] tasks = new ScheduledFuture[5];
+        for (int i = 0; i < tasks.length; i++)
+            tasks[i] = p.schedule(new SmallPossiblyInterruptedRunnable(),
+                                  LONG_DELAY_MS, MILLISECONDS);
+        try {
+            int max = tasks.length;
+            if (tasks[4].cancel(true)) --max;
+            if (tasks[3].cancel(true)) --max;
+            // There must eventually be an interference-free point at
+            // which purge will not fail. (At worst, when queue is empty.)
+            long startTime = System.nanoTime();
+            do {
+                p.purge();
+                long count = p.getTaskCount();
+                if (count == max)
+                    return;
+            } while (millisElapsedSince(startTime) < MEDIUM_DELAY_MS);
+            fail("Purge failed to remove cancelled tasks");
+        } finally {
+            for (ScheduledFuture task : tasks)
+                task.cancel(true);
+            joinPool(p);
+        }
+    }
+
+    /**
+     * shutdownNow returns a list containing tasks that were not run
+     */
+    public void testShutdownNow() {
+        CustomExecutor p = new CustomExecutor(1);
+        for (int i = 0; i < 5; i++)
+            p.schedule(new SmallPossiblyInterruptedRunnable(),
+                       LONG_DELAY_MS, MILLISECONDS);
+        try {
+            List<Runnable> l = p.shutdownNow();
+            assertTrue(p.isShutdown());
+            assertEquals(5, l.size());
+        } catch (SecurityException ok) {
+            // Allowed in case test doesn't have privs
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * In default setting, shutdown cancels periodic but not delayed
+     * tasks at shutdown
+     */
+    public void testShutdown1() throws InterruptedException {
+        CustomExecutor p = new CustomExecutor(1);
+        assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
+        assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
+
+        ScheduledFuture[] tasks = new ScheduledFuture[5];
+        for (int i = 0; i < tasks.length; i++)
+            tasks[i] = p.schedule(new NoOpRunnable(),
+                                  SHORT_DELAY_MS, MILLISECONDS);
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        BlockingQueue<Runnable> q = p.getQueue();
+        for (ScheduledFuture task : tasks) {
+            assertFalse(task.isDone());
+            assertFalse(task.isCancelled());
+            assertTrue(q.contains(task));
+        }
+        assertTrue(p.isShutdown());
+        assertTrue(p.awaitTermination(SMALL_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        for (ScheduledFuture task : tasks) {
+            assertTrue(task.isDone());
+            assertFalse(task.isCancelled());
+        }
+    }
+
+    /**
+     * If setExecuteExistingDelayedTasksAfterShutdownPolicy is false,
+     * delayed tasks are cancelled at shutdown
+     */
+    public void testShutdown2() throws InterruptedException {
+        CustomExecutor p = new CustomExecutor(1);
+        p.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
+        assertFalse(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
+        assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
+        ScheduledFuture[] tasks = new ScheduledFuture[5];
+        for (int i = 0; i < tasks.length; i++)
+            tasks[i] = p.schedule(new NoOpRunnable(),
+                                  SHORT_DELAY_MS, MILLISECONDS);
+        BlockingQueue q = p.getQueue();
+        assertEquals(tasks.length, q.size());
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        assertTrue(p.isShutdown());
+        assertTrue(q.isEmpty());
+        assertTrue(p.awaitTermination(SMALL_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        for (ScheduledFuture task : tasks) {
+            assertTrue(task.isDone());
+            assertTrue(task.isCancelled());
+        }
+    }
+
+    /**
+     * If setContinueExistingPeriodicTasksAfterShutdownPolicy is set false,
+     * periodic tasks are cancelled at shutdown
+     */
+    public void testShutdown3() throws InterruptedException {
+        CustomExecutor p = new CustomExecutor(1);
+        assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
+        assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
+        p.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
+        assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
+        assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
+        long initialDelay = LONG_DELAY_MS;
+        ScheduledFuture task =
+            p.scheduleAtFixedRate(new NoOpRunnable(), initialDelay,
+                                  5, MILLISECONDS);
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        assertTrue(p.isShutdown());
+        assertTrue(p.getQueue().isEmpty());
+        assertTrue(task.isDone());
+        assertTrue(task.isCancelled());
+        joinPool(p);
+    }
+
+    /**
+     * if setContinueExistingPeriodicTasksAfterShutdownPolicy is true,
+     * periodic tasks are not cancelled at shutdown
+     */
+    public void testShutdown4() throws InterruptedException {
+        CustomExecutor p = new CustomExecutor(1);
+        final CountDownLatch counter = new CountDownLatch(2);
+        try {
+            p.setContinueExistingPeriodicTasksAfterShutdownPolicy(true);
+            assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
+            assertTrue(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
+            final Runnable r = new CheckedRunnable() {
+                public void realRun() {
+                    counter.countDown();
+                }};
+            ScheduledFuture task =
+                p.scheduleAtFixedRate(r, 1, 1, MILLISECONDS);
+            assertFalse(task.isDone());
+            assertFalse(task.isCancelled());
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertFalse(task.isCancelled());
+            assertFalse(p.isTerminated());
+            assertTrue(p.isShutdown());
+            assertTrue(counter.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertFalse(task.isCancelled());
+            assertTrue(task.cancel(false));
+            assertTrue(task.isDone());
+            assertTrue(task.isCancelled());
+            assertTrue(p.awaitTermination(SMALL_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+        }
+        finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * completed submit of callable returns result
+     */
+    public void testSubmitCallable() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        try {
+            Future<String> future = e.submit(new StringTask());
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * completed submit of runnable returns successfully
+     */
+    public void testSubmitRunnable() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        try {
+            Future<?> future = e.submit(new NoOpRunnable());
+            future.get();
+            assertTrue(future.isDone());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * completed submit of (runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        try {
+            Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NPE
+     */
+    public void testInvokeAny1() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        try {
+            e.invokeAny(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IAE
+     */
+    public void testInvokeAny2() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        try {
+            e.invokeAny(new ArrayList<Callable<String>>());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NPE if c has null elements
+     */
+    public void testInvokeAny3() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        ExecutorService e = new CustomExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(latchAwaitingStringTask(latch));
+        l.add(null);
+        try {
+            e.invokeAny(l);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            latch.countDown();
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testInvokeAny4() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        try {
+            e.invokeAny(l);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task
+     */
+    public void testInvokeAny5() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NPE
+     */
+    public void testInvokeAll1() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        try {
+            e.invokeAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        try {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NPE if c has null elements
+     */
+    public void testInvokeAll3() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        l.add(null);
+        try {
+            e.invokeAll(l);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * get of invokeAll(c) throws exception on failed task
+     */
+    public void testInvokeAll4() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        List<Future<String>> futures = e.invokeAll(l);
+        assertEquals(1, futures.size());
+        try {
+            futures.get(0).get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks
+     */
+    public void testInvokeAll5() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NPE
+     */
+    public void testTimedInvokeAny1() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        try {
+            e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(,,null) throws NPE
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IAE
+     */
+    public void testTimedInvokeAny2() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        try {
+            e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        ExecutorService e = new CustomExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(latchAwaitingStringTask(latch));
+        l.add(null);
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            latch.countDown();
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task
+     */
+    public void testTimedInvokeAny5() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NPE
+     */
+    public void testTimedInvokeAll1() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        try {
+            e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(,,null) throws NPE
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        try {
+            e.invokeAll(l, MEDIUM_DELAY_MS, null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        try {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAll3() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        l.add(null);
+        try {
+            e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        List<Future<String>> futures =
+            e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+        assertEquals(1, futures.size());
+        try {
+            futures.get(0).get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks
+     */
+    public void testTimedInvokeAll5() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(c) cancels tasks not completed by timeout
+     */
+    public void testTimedInvokeAll6() throws Exception {
+        ExecutorService e = new CustomExecutor(2);
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(Executors.callable(new MediumPossiblyInterruptedRunnable(), TEST_STRING));
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, SHORT_DELAY_MS, MILLISECONDS);
+            assertEquals(l.size(), futures.size());
+            for (Future future : futures)
+                assertTrue(future.isDone());
+            assertFalse(futures.get(0).isCancelled());
+            assertTrue(futures.get(1).isCancelled());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ScheduledExecutorTest.java b/jsr166-tests/src/test/java/jsr166/ScheduledExecutorTest.java
new file mode 100644
index 0000000..4eea2c9
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ScheduledExecutorTest.java
@@ -0,0 +1,1164 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+import java.util.concurrent.*;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ScheduledExecutorTest extends JSR166TestCase {
+
+    /**
+     * execute successfully executes a runnable
+     */
+    public void testExecute() throws InterruptedException {
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        final Runnable task = new CheckedRunnable() {
+            public void realRun() {
+                done.countDown();
+            }};
+        try {
+            p.execute(task);
+            assertTrue(done.await(SMALL_DELAY_MS, MILLISECONDS));
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * delayed schedule of callable successfully executes after delay
+     */
+    public void testSchedule1() throws Exception {
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        final long startTime = System.nanoTime();
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            Callable task = new CheckedCallable<Boolean>() {
+                public Boolean realCall() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                    return Boolean.TRUE;
+                }};
+            Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
+            assertSame(Boolean.TRUE, f.get());
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            assertTrue(done.await(0L, MILLISECONDS));
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * delayed schedule of runnable successfully executes after delay
+     */
+    public void testSchedule3() throws Exception {
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        final long startTime = System.nanoTime();
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
+            await(done);
+            assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * scheduleAtFixedRate executes runnable after given initial delay
+     */
+    public void testSchedule4() throws Exception {
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        final long startTime = System.nanoTime();
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            ScheduledFuture f =
+                p.scheduleAtFixedRate(task, timeoutMillis(),
+                                      LONG_DELAY_MS, MILLISECONDS);
+            await(done);
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            f.cancel(true);
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * scheduleWithFixedDelay executes runnable after given initial delay
+     */
+    public void testSchedule5() throws Exception {
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        final long startTime = System.nanoTime();
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            ScheduledFuture f =
+                p.scheduleWithFixedDelay(task, timeoutMillis(),
+                                         LONG_DELAY_MS, MILLISECONDS);
+            await(done);
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            f.cancel(true);
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    static class RunnableCounter implements Runnable {
+        AtomicInteger count = new AtomicInteger(0);
+        public void run() { count.getAndIncrement(); }
+    }
+
+    /**
+     * scheduleAtFixedRate executes series of tasks at given rate
+     */
+    public void testFixedRateSequence() throws InterruptedException {
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        RunnableCounter counter = new RunnableCounter();
+        ScheduledFuture h =
+            p.scheduleAtFixedRate(counter, 0, 1, MILLISECONDS);
+        delay(SMALL_DELAY_MS);
+        h.cancel(true);
+        int c = counter.count.get();
+        // By time scaling conventions, we must have at least
+        // an execution per SHORT delay, but no more than one SHORT more
+        assertTrue(c >= SMALL_DELAY_MS / SHORT_DELAY_MS);
+        assertTrue(c <= SMALL_DELAY_MS + SHORT_DELAY_MS);
+        joinPool(p);
+    }
+
+    /**
+     * scheduleWithFixedDelay executes series of tasks with given period
+     */
+    public void testFixedDelaySequence() throws InterruptedException {
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        RunnableCounter counter = new RunnableCounter();
+        ScheduledFuture h =
+            p.scheduleWithFixedDelay(counter, 0, 1, MILLISECONDS);
+        delay(SMALL_DELAY_MS);
+        h.cancel(true);
+        int c = counter.count.get();
+        assertTrue(c >= SMALL_DELAY_MS / SHORT_DELAY_MS);
+        assertTrue(c <= SMALL_DELAY_MS + SHORT_DELAY_MS);
+        joinPool(p);
+    }
+
+    /**
+     * execute(null) throws NPE
+     */
+    public void testExecuteNull() throws InterruptedException {
+        ScheduledThreadPoolExecutor se = null;
+        try {
+            se = new ScheduledThreadPoolExecutor(1);
+            se.execute(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+
+        joinPool(se);
+    }
+
+    /**
+     * schedule(null) throws NPE
+     */
+    public void testScheduleNull() throws InterruptedException {
+        ScheduledThreadPoolExecutor se = new ScheduledThreadPoolExecutor(1);
+        try {
+            TrackedCallable callable = null;
+            Future f = se.schedule(callable, SHORT_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+        joinPool(se);
+    }
+
+    /**
+     * execute throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule1_RejectedExecutionException() throws InterruptedException {
+        ScheduledThreadPoolExecutor se = new ScheduledThreadPoolExecutor(1);
+        try {
+            se.shutdown();
+            se.schedule(new NoOpRunnable(),
+                        MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (RejectedExecutionException success) {
+        } catch (SecurityException ok) {
+        }
+
+        joinPool(se);
+    }
+
+    /**
+     * schedule throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule2_RejectedExecutionException() throws InterruptedException {
+        ScheduledThreadPoolExecutor se = new ScheduledThreadPoolExecutor(1);
+        try {
+            se.shutdown();
+            se.schedule(new NoOpCallable(),
+                        MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (RejectedExecutionException success) {
+        } catch (SecurityException ok) {
+        }
+        joinPool(se);
+    }
+
+    /**
+     * schedule callable throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule3_RejectedExecutionException() throws InterruptedException {
+        ScheduledThreadPoolExecutor se = new ScheduledThreadPoolExecutor(1);
+        try {
+            se.shutdown();
+            se.schedule(new NoOpCallable(),
+                        MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (RejectedExecutionException success) {
+        } catch (SecurityException ok) {
+        }
+        joinPool(se);
+    }
+
+    /**
+     * scheduleAtFixedRate throws RejectedExecutionException if shutdown
+     */
+    public void testScheduleAtFixedRate1_RejectedExecutionException() throws InterruptedException {
+        ScheduledThreadPoolExecutor se = new ScheduledThreadPoolExecutor(1);
+        try {
+            se.shutdown();
+            se.scheduleAtFixedRate(new NoOpRunnable(),
+                                   MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (RejectedExecutionException success) {
+        } catch (SecurityException ok) {
+        }
+        joinPool(se);
+    }
+
+    /**
+     * scheduleWithFixedDelay throws RejectedExecutionException if shutdown
+     */
+    public void testScheduleWithFixedDelay1_RejectedExecutionException() throws InterruptedException {
+        ScheduledThreadPoolExecutor se = new ScheduledThreadPoolExecutor(1);
+        try {
+            se.shutdown();
+            se.scheduleWithFixedDelay(new NoOpRunnable(),
+                                      MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (RejectedExecutionException success) {
+        } catch (SecurityException ok) {
+        }
+        joinPool(se);
+    }
+
+    /**
+     * getActiveCount increases but doesn't overestimate, when a
+     * thread becomes active
+     */
+    public void testGetActiveCount() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(2);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getActiveCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getActiveCount());
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(1, p.getActiveCount());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getCompletedTaskCount increases, but doesn't overestimate,
+     * when tasks complete
+     */
+    public void testGetCompletedTaskCount() throws InterruptedException {
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(2);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch threadProceed = new CountDownLatch(1);
+        final CountDownLatch threadDone = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(0, p.getCompletedTaskCount());
+                    threadProceed.await();
+                    threadDone.countDown();
+                }});
+            await(threadStarted);
+            assertEquals(0, p.getCompletedTaskCount());
+            threadProceed.countDown();
+            threadDone.await();
+            long startTime = System.nanoTime();
+            while (p.getCompletedTaskCount() != 1) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    fail("timed out");
+                Thread.yield();
+            }
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getCorePoolSize returns size given in constructor if not otherwise set
+     */
+    public void testGetCorePoolSize() throws InterruptedException {
+        ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        assertEquals(1, p.getCorePoolSize());
+        joinPool(p);
+    }
+
+    /**
+     * getLargestPoolSize increases, but doesn't overestimate, when
+     * multiple threads active
+     */
+    public void testGetLargestPoolSize() throws InterruptedException {
+        final int THREADS = 3;
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(THREADS);
+        final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getLargestPoolSize());
+            for (int i = 0; i < THREADS; i++)
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadsStarted.countDown();
+                        done.await();
+                        assertEquals(THREADS, p.getLargestPoolSize());
+                    }});
+            assertTrue(threadsStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(THREADS, p.getLargestPoolSize());
+        } finally {
+            done.countDown();
+            joinPool(p);
+            assertEquals(THREADS, p.getLargestPoolSize());
+        }
+    }
+
+    /**
+     * getPoolSize increases, but doesn't overestimate, when threads
+     * become active
+     */
+    public void testGetPoolSize() throws InterruptedException {
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getPoolSize());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(1, p.getPoolSize());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getTaskCount increases, but doesn't overestimate, when tasks
+     * submitted
+     */
+    public void testGetTaskCount() throws InterruptedException {
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        final int TASKS = 5;
+        try {
+            assertEquals(0, p.getTaskCount());
+            for (int i = 0; i < TASKS; i++)
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        done.await();
+                    }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(TASKS, p.getTaskCount());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getThreadFactory returns factory in constructor if not set
+     */
+    public void testGetThreadFactory() throws InterruptedException {
+        ThreadFactory tf = new SimpleThreadFactory();
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1, tf);
+        assertSame(tf, p.getThreadFactory());
+        joinPool(p);
+    }
+
+    /**
+     * setThreadFactory sets the thread factory returned by getThreadFactory
+     */
+    public void testSetThreadFactory() throws InterruptedException {
+        ThreadFactory tf = new SimpleThreadFactory();
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        p.setThreadFactory(tf);
+        assertSame(tf, p.getThreadFactory());
+        joinPool(p);
+    }
+
+    /**
+     * setThreadFactory(null) throws NPE
+     */
+    public void testSetThreadFactoryNull() throws InterruptedException {
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try {
+            p.setThreadFactory(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * isShutdown is false before shutdown, true after
+     */
+    public void testIsShutdown() {
+
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try {
+            assertFalse(p.isShutdown());
+        }
+        finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        assertTrue(p.isShutdown());
+    }
+
+    /**
+     * isTerminated is false before termination, true after
+     */
+    public void testIsTerminated() throws InterruptedException {
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        assertFalse(p.isTerminated());
+        try {
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminated());
+                    threadStarted.countDown();
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertFalse(p.isTerminating());
+            done.countDown();
+        } finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+    }
+
+    /**
+     * isTerminating is not true when running or when terminated
+     */
+    public void testIsTerminating() throws InterruptedException {
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertFalse(p.isTerminating());
+            done.countDown();
+        } finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertFalse(p.isTerminating());
+    }
+
+    /**
+     * getQueue returns the work queue, which contains queued tasks
+     */
+    public void testGetQueue() throws InterruptedException {
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            ScheduledFuture[] tasks = new ScheduledFuture[5];
+            for (int i = 0; i < tasks.length; i++) {
+                Runnable r = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        done.await();
+                    }};
+                tasks[i] = p.schedule(r, 1, MILLISECONDS);
+            }
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            BlockingQueue<Runnable> q = p.getQueue();
+            assertTrue(q.contains(tasks[tasks.length - 1]));
+            assertFalse(q.contains(tasks[0]));
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * remove(task) removes queued task, and fails to remove active task
+     */
+    public void testRemove() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        ScheduledFuture[] tasks = new ScheduledFuture[5];
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            for (int i = 0; i < tasks.length; i++) {
+                Runnable r = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        done.await();
+                    }};
+                tasks[i] = p.schedule(r, 1, MILLISECONDS);
+            }
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            BlockingQueue<Runnable> q = p.getQueue();
+            assertFalse(p.remove((Runnable)tasks[0]));
+            assertTrue(q.contains((Runnable)tasks[4]));
+            assertTrue(q.contains((Runnable)tasks[3]));
+            assertTrue(p.remove((Runnable)tasks[4]));
+            assertFalse(p.remove((Runnable)tasks[4]));
+            assertFalse(q.contains((Runnable)tasks[4]));
+            assertTrue(q.contains((Runnable)tasks[3]));
+            assertTrue(p.remove((Runnable)tasks[3]));
+            assertFalse(q.contains((Runnable)tasks[3]));
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * purge eventually removes cancelled tasks from the queue
+     */
+    public void testPurge() throws InterruptedException {
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        ScheduledFuture[] tasks = new ScheduledFuture[5];
+        for (int i = 0; i < tasks.length; i++)
+            tasks[i] = p.schedule(new SmallPossiblyInterruptedRunnable(),
+                                  LONG_DELAY_MS, MILLISECONDS);
+        try {
+            int max = tasks.length;
+            if (tasks[4].cancel(true)) --max;
+            if (tasks[3].cancel(true)) --max;
+            // There must eventually be an interference-free point at
+            // which purge will not fail. (At worst, when queue is empty.)
+            long startTime = System.nanoTime();
+            do {
+                p.purge();
+                long count = p.getTaskCount();
+                if (count == max)
+                    return;
+            } while (millisElapsedSince(startTime) < MEDIUM_DELAY_MS);
+            fail("Purge failed to remove cancelled tasks");
+        } finally {
+            for (ScheduledFuture task : tasks)
+                task.cancel(true);
+            joinPool(p);
+        }
+    }
+
+    /**
+     * shutdownNow returns a list containing tasks that were not run
+     */
+    public void testShutdownNow() {
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        for (int i = 0; i < 5; i++)
+            p.schedule(new SmallPossiblyInterruptedRunnable(),
+                       LONG_DELAY_MS, MILLISECONDS);
+        try {
+            List<Runnable> l = p.shutdownNow();
+            assertTrue(p.isShutdown());
+            assertEquals(5, l.size());
+        } catch (SecurityException ok) {
+            // Allowed in case test doesn't have privs
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * In default setting, shutdown cancels periodic but not delayed
+     * tasks at shutdown
+     */
+    public void testShutdown1() throws InterruptedException {
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
+        assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
+
+        ScheduledFuture[] tasks = new ScheduledFuture[5];
+        for (int i = 0; i < tasks.length; i++)
+            tasks[i] = p.schedule(new NoOpRunnable(),
+                                  SHORT_DELAY_MS, MILLISECONDS);
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        BlockingQueue<Runnable> q = p.getQueue();
+        for (ScheduledFuture task : tasks) {
+            assertFalse(task.isDone());
+            assertFalse(task.isCancelled());
+            assertTrue(q.contains(task));
+        }
+        assertTrue(p.isShutdown());
+        assertTrue(p.awaitTermination(SMALL_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        for (ScheduledFuture task : tasks) {
+            assertTrue(task.isDone());
+            assertFalse(task.isCancelled());
+        }
+    }
+
+    /**
+     * If setExecuteExistingDelayedTasksAfterShutdownPolicy is false,
+     * delayed tasks are cancelled at shutdown
+     */
+    public void testShutdown2() throws InterruptedException {
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        p.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
+        assertFalse(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
+        assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
+        ScheduledFuture[] tasks = new ScheduledFuture[5];
+        for (int i = 0; i < tasks.length; i++)
+            tasks[i] = p.schedule(new NoOpRunnable(),
+                                  SHORT_DELAY_MS, MILLISECONDS);
+        BlockingQueue q = p.getQueue();
+        assertEquals(tasks.length, q.size());
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        assertTrue(p.isShutdown());
+        assertTrue(q.isEmpty());
+        assertTrue(p.awaitTermination(SMALL_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        for (ScheduledFuture task : tasks) {
+            assertTrue(task.isDone());
+            assertTrue(task.isCancelled());
+        }
+    }
+
+    /**
+     * If setContinueExistingPeriodicTasksAfterShutdownPolicy is set false,
+     * periodic tasks are cancelled at shutdown
+     */
+    public void testShutdown3() throws InterruptedException {
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
+        assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
+        p.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
+        assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
+        assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
+        long initialDelay = LONG_DELAY_MS;
+        ScheduledFuture task =
+            p.scheduleAtFixedRate(new NoOpRunnable(), initialDelay,
+                                  5, MILLISECONDS);
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        assertTrue(p.isShutdown());
+        assertTrue(p.getQueue().isEmpty());
+        assertTrue(task.isDone());
+        assertTrue(task.isCancelled());
+        joinPool(p);
+    }
+
+    /**
+     * if setContinueExistingPeriodicTasksAfterShutdownPolicy is true,
+     * periodic tasks are not cancelled at shutdown
+     */
+    public void testShutdown4() throws InterruptedException {
+        ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        final CountDownLatch counter = new CountDownLatch(2);
+        try {
+            p.setContinueExistingPeriodicTasksAfterShutdownPolicy(true);
+            assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
+            assertTrue(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
+            final Runnable r = new CheckedRunnable() {
+                public void realRun() {
+                    counter.countDown();
+                }};
+            ScheduledFuture task =
+                p.scheduleAtFixedRate(r, 1, 1, MILLISECONDS);
+            assertFalse(task.isDone());
+            assertFalse(task.isCancelled());
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertFalse(task.isCancelled());
+            assertFalse(p.isTerminated());
+            assertTrue(p.isShutdown());
+            assertTrue(counter.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertFalse(task.isCancelled());
+            assertTrue(task.cancel(false));
+            assertTrue(task.isDone());
+            assertTrue(task.isCancelled());
+            assertTrue(p.awaitTermination(SMALL_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+        }
+        finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * completed submit of callable returns result
+     */
+    public void testSubmitCallable() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try {
+            Future<String> future = e.submit(new StringTask());
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * completed submit of runnable returns successfully
+     */
+    public void testSubmitRunnable() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try {
+            Future<?> future = e.submit(new NoOpRunnable());
+            future.get();
+            assertTrue(future.isDone());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * completed submit of (runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try {
+            Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NPE
+     */
+    public void testInvokeAny1() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try {
+            e.invokeAny(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IAE
+     */
+    public void testInvokeAny2() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try {
+            e.invokeAny(new ArrayList<Callable<String>>());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NPE if c has null elements
+     */
+    public void testInvokeAny3() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(latchAwaitingStringTask(latch));
+        l.add(null);
+        try {
+            e.invokeAny(l);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            latch.countDown();
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testInvokeAny4() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        try {
+            e.invokeAny(l);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task
+     */
+    public void testInvokeAny5() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NPE
+     */
+    public void testInvokeAll1() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try {
+            e.invokeAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NPE if c has null elements
+     */
+    public void testInvokeAll3() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        l.add(null);
+        try {
+            e.invokeAll(l);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * get of invokeAll(c) throws exception on failed task
+     */
+    public void testInvokeAll4() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        List<Future<String>> futures = e.invokeAll(l);
+        assertEquals(1, futures.size());
+        try {
+            futures.get(0).get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks
+     */
+    public void testInvokeAll5() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NPE
+     */
+    public void testTimedInvokeAny1() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try {
+            e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(,,null) throws NPE
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IAE
+     */
+    public void testTimedInvokeAny2() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try {
+            e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(latchAwaitingStringTask(latch));
+        l.add(null);
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            latch.countDown();
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task
+     */
+    public void testTimedInvokeAny5() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NPE
+     */
+    public void testTimedInvokeAll1() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try {
+            e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(,,null) throws NPE
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        try {
+            e.invokeAll(l, MEDIUM_DELAY_MS, null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAll3() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        l.add(null);
+        try {
+            e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        List<Future<String>> futures =
+            e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+        assertEquals(1, futures.size());
+        try {
+            futures.get(0).get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks
+     */
+    public void testTimedInvokeAll5() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(c) cancels tasks not completed by timeout
+     */
+    public void testTimedInvokeAll6() throws Exception {
+        ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(Executors.callable(new MediumPossiblyInterruptedRunnable(), TEST_STRING));
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, SHORT_DELAY_MS, MILLISECONDS);
+            assertEquals(l.size(), futures.size());
+            for (Future future : futures)
+                assertTrue(future.isDone());
+            assertFalse(futures.get(0).isCancelled());
+            assertTrue(futures.get(1).isCancelled());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/SemaphoreTest.java b/jsr166-tests/src/test/java/jsr166/SemaphoreTest.java
new file mode 100644
index 0000000..f303285
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/SemaphoreTest.java
@@ -0,0 +1,629 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class SemaphoreTest extends JSR166TestCase {
+
+    /**
+     * Subclass to expose protected methods
+     */
+    static class PublicSemaphore extends Semaphore {
+        PublicSemaphore(int permits) { super(permits); }
+        PublicSemaphore(int permits, boolean fair) { super(permits, fair); }
+        public Collection<Thread> 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
+     */
+    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 AssertionFailedError("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 AssertionFailedError("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));
+            }
+        },
+        tryAcquireTimedN {
+            void acquire(Semaphore s, int permits) throws InterruptedException {
+                assertTrue(s.tryAcquire(permits, 2 * LONG_DELAY_MS, MILLISECONDS));
+            }
+        };
+
+        // 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);
+        }
+    }
+
+    /**
+     * Zero, negative, and positive initial values are allowed in constructor
+     */
+    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 testTryAcquire_timeout()      { testTryAcquire_timeout(false); }
+    public void testTryAcquire_timeout_fair() { testTryAcquire_timeout(true); }
+    public void testTryAcquire_timeout(boolean fair) {
+        Semaphore s = new Semaphore(0, fair);
+        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()      { testTryAcquireN_timeout(false); }
+    public void testTryAcquireN_timeout_fair() { testTryAcquireN_timeout(true); }
+    public void testTryAcquireN_timeout(boolean fair) {
+        Semaphore s = new Semaphore(2, fair);
+        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 Semaphore pleaseInterrupt = new Semaphore(0, fair);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                // Interrupt before acquire
+                Thread.currentThread().interrupt();
+                try {
+                    acquirer.acquire(s);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+
+                // Interrupt during acquire
+                try {
+                    acquirer.acquire(s);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+
+                // Interrupt before acquire(N)
+                Thread.currentThread().interrupt();
+                try {
+                    acquirer.acquire(s, 3);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+
+                pleaseInterrupt.release();
+
+                // Interrupt during acquire(N)
+                try {
+                    acquirer.acquire(s, 3);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        waitForQueuedThread(s, t);
+        t.interrupt();
+        await(pleaseInterrupt);
+        waitForQueuedThread(s, t);
+        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();
+
+        assertThreadStaysAlive(t1);
+        assertTrue(t2.isAlive());
+
+        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 testReleaseIAE()      { testReleaseIAE(false); }
+    public void testReleaseIAE_fair() { testReleaseIAE(true); }
+    public void testReleaseIAE(boolean fair) {
+        Semaphore s = new Semaphore(10, fair);
+        try {
+            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 testSerialization()      { testSerialization(false); }
+    public void testSerialization_fair() { testSerialization(true); }
+    public void testSerialization(boolean fair) {
+        try {
+            Semaphore s = new Semaphore(3, fair);
+            s.acquire();
+            s.acquire();
+            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());
+
+            s = new Semaphore(0, fair);
+            Thread t = newStartedThread(new InterruptibleLockRunnable(s));
+            waitForQueuedThreads(s);
+            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());
+        } catch (InterruptedException e) { threadUnexpectedException(e); }
+    }
+
+    /**
+     * tryAcquire(n) succeeds when sufficient permits, else fails
+     */
+    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 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());
+        }
+    }
+
+    /**
+     * 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 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);
+            }});
+
+        waitForQueuedThreads(s);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                // Will fail, even though 1 permit is available
+                assertFalse(s.tryAcquire(0L, MILLISECONDS));
+                assertFalse(s.tryAcquire(1, 0L, MILLISECONDS));
+
+                // 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"));
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/SynchronousQueueTest.java b/jsr166-tests/src/test/java/jsr166/SynchronousQueueTest.java
new file mode 100644
index 0000000..711b47b
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/SynchronousQueueTest.java
@@ -0,0 +1,601 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.SynchronousQueue;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class SynchronousQueueTest extends JSR166TestCase {
+
+    public static class Fair extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new SynchronousQueue(true);
+        }
+    }
+
+    public static class NonFair extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new SynchronousQueue(false);
+        }
+    }
+
+    /**
+     * Any SynchronousQueue is both empty and full
+     */
+    public void testEmptyFull()      { testEmptyFull(false); }
+    public void testEmptyFull_fair() { testEmptyFull(true); }
+    public void testEmptyFull(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertEquals(0, q.remainingCapacity());
+        assertFalse(q.offer(zero));
+    }
+
+    /**
+     * offer fails if no active taker
+     */
+    public void testOffer()      { testOffer(false); }
+    public void testOffer_fair() { testOffer(true); }
+    public void testOffer(boolean fair) {
+        SynchronousQueue q = new SynchronousQueue(fair);
+        assertFalse(q.offer(one));
+    }
+
+    /**
+     * add throws IllegalStateException if no active taker
+     */
+    public void testAdd()      { testAdd(false); }
+    public void testAdd_fair() { testAdd(true); }
+    public void testAdd(boolean fair) {
+        SynchronousQueue q = new SynchronousQueue(fair);
+        assertEquals(0, q.remainingCapacity());
+        try {
+            q.add(one);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * addAll(this) throws IllegalArgumentException
+     */
+    public void testAddAll_self()      { testAddAll_self(false); }
+    public void testAddAll_self_fair() { testAddAll_self(true); }
+    public void testAddAll_self(boolean fair) {
+        SynchronousQueue q = new SynchronousQueue(fair);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll throws ISE if no active taker
+     */
+    public void testAddAll_ISE()      { testAddAll_ISE(false); }
+    public void testAddAll_ISE_fair() { testAddAll_ISE(true); }
+    public void testAddAll_ISE(boolean fair) {
+        SynchronousQueue q = new SynchronousQueue(fair);
+        Integer[] ints = new Integer[1];
+        for (int i = 0; i < ints.length; i++)
+            ints[i] = i;
+        Collection<Integer> coll = Arrays.asList(ints);
+        try {
+            q.addAll(coll);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * put blocks interruptibly if no active taker
+     */
+    public void testBlockingPut()      { testBlockingPut(false); }
+    public void testBlockingPut_fair() { testBlockingPut(true); }
+    public void testBlockingPut(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Thread.currentThread().interrupt();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly waiting for take
+     */
+    public void testPutWithTake()      { testPutWithTake(false); }
+    public void testPutWithTake_fair() { testPutWithTake(true); }
+    public void testPutWithTake(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                pleaseTake.countDown();
+                q.put(one);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        try { assertSame(one, q.take()); }
+        catch (InterruptedException e) { threadUnexpectedException(e); }
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offer times out if elements not taken
+     */
+    public void testTimedOffer()      { testTimedOffer(false); }
+    public void testTimedOffer_fair() { testTimedOffer(true); }
+    public void testTimedOffer(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll return null if no active putter
+     */
+    public void testPoll()      { testPoll(false); }
+    public void testPoll_fair() { testPoll(true); }
+    public void testPoll(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout times out if no active putter
+     */
+    public void testTimedPoll0()      { testTimedPoll0(false); }
+    public void testTimedPoll0_fair() { testTimedPoll0(true); }
+    public void testTimedPoll0(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        try { assertNull(q.poll(0, MILLISECONDS)); }
+        catch (InterruptedException e) { threadUnexpectedException(e); }
+    }
+
+    /**
+     * timed poll with nonzero timeout times out if no active putter
+     */
+    public void testTimedPoll()      { testTimedPoll(false); }
+    public void testTimedPoll_fair() { testTimedPoll(true); }
+    public void testTimedPoll(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        long startTime = System.nanoTime();
+        try { assertNull(q.poll(timeoutMillis(), MILLISECONDS)); }
+        catch (InterruptedException e) { threadUnexpectedException(e); }
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+    }
+
+    /**
+     * timed poll before a delayed offer times out, returning null;
+     * after offer succeeds; on interruption throws
+     */
+    public void testTimedPollWithOffer()      { testTimedPollWithOffer(false); }
+    public void testTimedPollWithOffer_fair() { testTimedPollWithOffer(true); }
+    public void testTimedPollWithOffer(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CountDownLatch pleaseOffer = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+
+                pleaseOffer.countDown();
+                startTime = System.nanoTime();
+                assertSame(zero, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) < MEDIUM_DELAY_MS);
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseOffer);
+        long startTime = System.nanoTime();
+        try { assertTrue(q.offer(zero, LONG_DELAY_MS, MILLISECONDS)); }
+        catch (InterruptedException e) { threadUnexpectedException(e); }
+        assertTrue(millisElapsedSince(startTime) < MEDIUM_DELAY_MS);
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * peek() returns null if no active putter
+     */
+    public void testPeek()      { testPeek(false); }
+    public void testPeek_fair() { testPeek(true); }
+    public void testPeek(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        assertNull(q.peek());
+    }
+
+    /**
+     * element() throws NoSuchElementException if no active putter
+     */
+    public void testElement()      { testElement(false); }
+    public void testElement_fair() { testElement(true); }
+    public void testElement(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove() throws NoSuchElementException if no active putter
+     */
+    public void testRemove()      { testRemove(false); }
+    public void testRemove_fair() { testRemove(true); }
+    public void testRemove(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * contains returns false
+     */
+    public void testContains()      { testContains(false); }
+    public void testContains_fair() { testContains(true); }
+    public void testContains(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        assertFalse(q.contains(zero));
+    }
+
+    /**
+     * clear ensures isEmpty
+     */
+    public void testClear()      { testClear(false); }
+    public void testClear_fair() { testClear(true); }
+    public void testClear(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll returns false unless empty
+     */
+    public void testContainsAll()      { testContainsAll(false); }
+    public void testContainsAll_fair() { testContainsAll(true); }
+    public void testContainsAll(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Integer[] empty = new Integer[0];
+        assertTrue(q.containsAll(Arrays.asList(empty)));
+        Integer[] ints = new Integer[1]; ints[0] = zero;
+        assertFalse(q.containsAll(Arrays.asList(ints)));
+    }
+
+    /**
+     * retainAll returns false
+     */
+    public void testRetainAll()      { testRetainAll(false); }
+    public void testRetainAll_fair() { testRetainAll(true); }
+    public void testRetainAll(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Integer[] empty = new Integer[0];
+        assertFalse(q.retainAll(Arrays.asList(empty)));
+        Integer[] ints = new Integer[1]; ints[0] = zero;
+        assertFalse(q.retainAll(Arrays.asList(ints)));
+    }
+
+    /**
+     * removeAll returns false
+     */
+    public void testRemoveAll()      { testRemoveAll(false); }
+    public void testRemoveAll_fair() { testRemoveAll(true); }
+    public void testRemoveAll(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Integer[] empty = new Integer[0];
+        assertFalse(q.removeAll(Arrays.asList(empty)));
+        Integer[] ints = new Integer[1]; ints[0] = zero;
+        assertFalse(q.containsAll(Arrays.asList(ints)));
+    }
+
+    /**
+     * toArray is empty
+     */
+    public void testToArray()      { testToArray(false); }
+    public void testToArray_fair() { testToArray(true); }
+    public void testToArray(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Object[] o = q.toArray();
+        assertEquals(0, o.length);
+    }
+
+    /**
+     * toArray(Integer array) returns its argument with the first
+     * element (if present) nulled out
+     */
+    public void testToArray2()      { testToArray2(false); }
+    public void testToArray2_fair() { testToArray2(true); }
+    public void testToArray2(boolean fair) {
+        final SynchronousQueue<Integer> q
+            = new SynchronousQueue<Integer>(fair);
+        Integer[] a;
+
+        a = new Integer[0];
+        assertSame(a, q.toArray(a));
+
+        a = new Integer[3];
+        Arrays.fill(a, 42);
+        assertSame(a, q.toArray(a));
+        assertNull(a[0]);
+        for (int i = 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+    }
+
+    /**
+     * toArray(null) throws NPE
+     */
+    public void testToArray_null()      { testToArray_null(false); }
+    public void testToArray_null_fair() { testToArray_null(true); }
+    public void testToArray_null(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        try {
+            Object o[] = q.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * iterator does not traverse any elements
+     */
+    public void testIterator()      { testIterator(false); }
+    public void testIterator_fair() { testIterator(true); }
+    public void testIterator(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Iterator it = q.iterator();
+        assertFalse(it.hasNext());
+        try {
+            Object x = it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * iterator remove throws ISE
+     */
+    public void testIteratorRemove()      { testIteratorRemove(false); }
+    public void testIteratorRemove_fair() { testIteratorRemove(true); }
+    public void testIteratorRemove(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Iterator it = q.iterator();
+        try {
+            it.remove();
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * toString returns a non-null string
+     */
+    public void testToString()      { testToString(false); }
+    public void testToString_fair() { testToString(true); }
+    public void testToString(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        String s = q.toString();
+        assertNotNull(s);
+    }
+
+    /**
+     * offer transfers elements across Executor tasks
+     */
+    public void testOfferInExecutor()      { testOfferInExecutor(false); }
+    public void testOfferInExecutor_fair() { testOfferInExecutor(true); }
+    public void testOfferInExecutor(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        ExecutorService executor = Executors.newFixedThreadPool(2);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(q.offer(one));
+                threadsStarted.await();
+                assertTrue(q.offer(one, LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(0, q.remainingCapacity());
+            }});
+
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadsStarted.await();
+                assertSame(one, q.take());
+            }});
+
+        joinPool(executor);
+    }
+
+    /**
+     * timed poll retrieves elements across Executor threads
+     */
+    public void testPollInExecutor()      { testPollInExecutor(false); }
+    public void testPollInExecutor_fair() { testPollInExecutor(true); }
+    public void testPollInExecutor(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        ExecutorService executor = Executors.newFixedThreadPool(2);
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertNull(q.poll());
+                threadsStarted.await();
+                assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                assertTrue(q.isEmpty());
+            }});
+
+        executor.execute(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadsStarted.await();
+                q.put(one);
+            }});
+
+        joinPool(executor);
+    }
+
+    /**
+     * a deserialized serialized queue is usable
+     */
+    public void testSerialization() {
+        final SynchronousQueue x = new SynchronousQueue();
+        final SynchronousQueue y = new SynchronousQueue(false);
+        final SynchronousQueue z = new SynchronousQueue(true);
+        assertSerialEquals(x, y);
+        assertNotSerialEquals(x, z);
+        SynchronousQueue[] qs = { x, y, z };
+        for (SynchronousQueue q : qs) {
+            SynchronousQueue clone = serialClone(q);
+            assertNotSame(q, clone);
+            assertSerialEquals(q, clone);
+            assertTrue(clone.isEmpty());
+            assertEquals(0, clone.size());
+            assertEquals(0, clone.remainingCapacity());
+            assertFalse(clone.offer(zero));
+        }
+    }
+
+    /**
+     * drainTo(c) of empty queue doesn't transfer elements
+     */
+    public void testDrainTo()      { testDrainTo(false); }
+    public void testDrainTo_fair() { testDrainTo(true); }
+    public void testDrainTo(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(0, l.size());
+    }
+
+    /**
+     * drainTo empties queue, unblocking a waiting put.
+     */
+    public void testDrainToWithActivePut()      { testDrainToWithActivePut(false); }
+    public void testDrainToWithActivePut_fair() { testDrainToWithActivePut(true); }
+    public void testDrainToWithActivePut(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(one);
+            }});
+
+        ArrayList l = new ArrayList();
+        long startTime = System.nanoTime();
+        while (l.isEmpty()) {
+            q.drainTo(l);
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                fail("timed out");
+            Thread.yield();
+        }
+        assertTrue(l.size() == 1);
+        assertSame(one, l.get(0));
+        awaitTermination(t);
+    }
+
+    /**
+     * drainTo(c, n) empties up to n elements of queue into c
+     */
+    public void testDrainToN() throws InterruptedException {
+        final SynchronousQueue q = new SynchronousQueue();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(one);
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(two);
+            }});
+
+        ArrayList l = new ArrayList();
+        delay(SHORT_DELAY_MS);
+        q.drainTo(l, 1);
+        assertEquals(1, l.size());
+        q.drainTo(l, 1);
+        assertEquals(2, l.size());
+        assertTrue(l.contains(one));
+        assertTrue(l.contains(two));
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/SystemTest.java b/jsr166-tests/src/test/java/jsr166/SystemTest.java
new file mode 100644
index 0000000..32caec2
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/SystemTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+
+public class SystemTest extends JSR166TestCase {
+
+    /**
+     * Worst case rounding for millisecs; set for 60 cycle millis clock.
+     * This value might need to be changed on JVMs with coarser
+     * System.currentTimeMillis clocks.
+     */
+    static final long MILLIS_ROUND = 17;
+
+    /**
+     * Nanos between readings of millis is no longer than millis (plus
+     * possible rounding).
+     * This shows only that nano timing not (much) worse than milli.
+     */
+    public void testNanoTime1() throws InterruptedException {
+        long m1 = System.currentTimeMillis();
+        Thread.sleep(1);
+        long n1 = System.nanoTime();
+        Thread.sleep(SHORT_DELAY_MS);
+        long n2 = System.nanoTime();
+        Thread.sleep(1);
+        long m2 = System.currentTimeMillis();
+        long millis = m2 - m1;
+        long nanos = n2 - n1;
+        assertTrue(nanos >= 0);
+        long nanosAsMillis = nanos / 1000000;
+        assertTrue(nanosAsMillis <= millis + MILLIS_ROUND);
+    }
+
+    /**
+     * Millis between readings of nanos is less than nanos, adjusting
+     * for rounding.
+     * This shows only that nano timing not (much) worse than milli.
+     */
+    public void testNanoTime2() throws InterruptedException {
+        long n1 = System.nanoTime();
+        Thread.sleep(1);
+        long m1 = System.currentTimeMillis();
+        Thread.sleep(SHORT_DELAY_MS);
+        long m2 = System.currentTimeMillis();
+        Thread.sleep(1);
+        long n2 = System.nanoTime();
+        long millis = m2 - m1;
+        long nanos = n2 - n1;
+
+        assertTrue(nanos >= 0);
+        long nanosAsMillis = nanos / 1000000;
+        assertTrue(millis <= nanosAsMillis + MILLIS_ROUND);
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ThreadLocalRandomTest.java b/jsr166-tests/src/test/java/jsr166/ThreadLocalRandomTest.java
new file mode 100644
index 0000000..665a2b7
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ThreadLocalRandomTest.java
@@ -0,0 +1,290 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class ThreadLocalRandomTest extends JSR166TestCase {
+
+    /*
+     * Testing coverage notes:
+     *
+     * We don't test randomness properties, but only that repeated
+     * calls, up to NCALLS tries, produce at least one different
+     * result.  For bounded versions, we sample various intervals
+     * across multiples of primes.
+     */
+
+    //
+    static final int NCALLS = 10000;
+
+    // max sampled int bound
+    static final int MAX_INT_BOUND = (1 << 28);
+
+    // Max sampled long bound
+    static final long MAX_LONG_BOUND = (1L << 42);
+
+    /**
+     * setSeed throws UnsupportedOperationException
+     */
+    public void testSetSeed() {
+        try {
+            ThreadLocalRandom.current().setSeed(17);
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+
+    /**
+     * Repeated calls to nextInt produce at least one different result
+     */
+    public void testNextInt() {
+        int f = ThreadLocalRandom.current().nextInt();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextInt() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextLong produce at least one different result
+     */
+    public void testNextLong() {
+        long f = ThreadLocalRandom.current().nextLong();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextLong() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextBoolean produce at least one different result
+     */
+    public void testNextBoolean() {
+        boolean f = ThreadLocalRandom.current().nextBoolean();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextBoolean() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextFloat produce at least one different result
+     */
+    public void testNextFloat() {
+        float f = ThreadLocalRandom.current().nextFloat();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextFloat() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextDouble produce at least one different result
+     */
+    public void testNextDouble() {
+        double f = ThreadLocalRandom.current().nextDouble();
+        double i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextDouble() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextGaussian produce at least one different result
+     */
+    public void testNextGaussian() {
+        double f = ThreadLocalRandom.current().nextGaussian();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextGaussian() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * nextInt(negative) throws IllegalArgumentException;
+     */
+    public void testNextIntBoundedNeg() {
+        try {
+            int f = ThreadLocalRandom.current().nextInt(-17);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * nextInt(least >= bound) throws IllegalArgumentException;
+     */
+    public void testNextIntBadBounds() {
+        try {
+            int f = ThreadLocalRandom.current().nextInt(17, 2);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * nextInt(bound) returns 0 <= value < bound;
+     * repeated calls produce at least one different result
+     */
+    public void testNextIntBounded() {
+        // sample bound space across prime number increments
+        for (int bound = 2; bound < MAX_INT_BOUND; bound += 524959) {
+            int f = ThreadLocalRandom.current().nextInt(bound);
+            assertTrue(0 <= f && f < bound);
+            int i = 0;
+            int j;
+            while (i < NCALLS &&
+                   (j = ThreadLocalRandom.current().nextInt(bound)) == f) {
+                assertTrue(0 <= j && j < bound);
+                ++i;
+            }
+            assertTrue(i < NCALLS);
+        }
+    }
+
+    /**
+     * nextInt(least, bound) returns least <= value < bound;
+     * repeated calls produce at least one different result
+     */
+    public void testNextIntBounded2() {
+        for (int least = -15485863; least < MAX_INT_BOUND; least += 524959) {
+            for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 49979687) {
+                int f = ThreadLocalRandom.current().nextInt(least, bound);
+                assertTrue(least <= f && f < bound);
+                int i = 0;
+                int j;
+                while (i < NCALLS &&
+                       (j = ThreadLocalRandom.current().nextInt(least, bound)) == f) {
+                    assertTrue(least <= j && j < bound);
+                    ++i;
+                }
+                assertTrue(i < NCALLS);
+            }
+        }
+    }
+
+    /**
+     * nextLong(negative) throws IllegalArgumentException;
+     */
+    public void testNextLongBoundedNeg() {
+        try {
+            long f = ThreadLocalRandom.current().nextLong(-17);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * nextLong(least >= bound) throws IllegalArgumentException;
+     */
+    public void testNextLongBadBounds() {
+        try {
+            long f = ThreadLocalRandom.current().nextLong(17, 2);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * nextLong(bound) returns 0 <= value < bound;
+     * repeated calls produce at least one different result
+     */
+    public void testNextLongBounded() {
+        for (long bound = 2; bound < MAX_LONG_BOUND; bound += 15485863) {
+            long f = ThreadLocalRandom.current().nextLong(bound);
+            assertTrue(0 <= f && f < bound);
+            int i = 0;
+            long j;
+            while (i < NCALLS &&
+                   (j = ThreadLocalRandom.current().nextLong(bound)) == f) {
+                assertTrue(0 <= j && j < bound);
+                ++i;
+            }
+            assertTrue(i < NCALLS);
+        }
+    }
+
+    /**
+     * nextLong(least, bound) returns least <= value < bound;
+     * repeated calls produce at least one different result
+     */
+    public void testNextLongBounded2() {
+        for (long least = -86028121; least < MAX_LONG_BOUND; least += 982451653L) {
+            for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
+                long f = ThreadLocalRandom.current().nextLong(least, bound);
+                assertTrue(least <= f && f < bound);
+                int i = 0;
+                long j;
+                while (i < NCALLS &&
+                       (j = ThreadLocalRandom.current().nextLong(least, bound)) == f) {
+                    assertTrue(least <= j && j < bound);
+                    ++i;
+                }
+                assertTrue(i < NCALLS);
+            }
+        }
+    }
+
+    /**
+     * nextDouble(least, bound) returns least <= value < bound;
+     * repeated calls produce at least one different result
+     */
+    public void testNextDoubleBounded2() {
+        for (double least = 0.0001; least < 1.0e20; least *= 8) {
+            for (double bound = least * 1.001; bound < 1.0e20; bound *= 16) {
+                double f = ThreadLocalRandom.current().nextDouble(least, bound);
+                assertTrue(least <= f && f < bound);
+                int i = 0;
+                double j;
+                while (i < NCALLS &&
+                       (j = ThreadLocalRandom.current().nextDouble(least, bound)) == f) {
+                    assertTrue(least <= j && j < bound);
+                    ++i;
+                }
+                assertTrue(i < NCALLS);
+            }
+        }
+    }
+
+    /**
+     * Different threads produce different pseudo-random sequences
+     */
+    public void testDifferentSequences() {
+        // Don't use main thread's ThreadLocalRandom - it is likely to
+        // be polluted by previous tests.
+        final AtomicReference<ThreadLocalRandom> threadLocalRandom =
+            new AtomicReference<ThreadLocalRandom>();
+        final AtomicLong rand = new AtomicLong();
+
+        long firstRand = 0;
+        ThreadLocalRandom firstThreadLocalRandom = null;
+
+        final CheckedRunnable getRandomState = new CheckedRunnable() {
+            public void realRun() {
+                ThreadLocalRandom current = ThreadLocalRandom.current();
+                assertSame(current, ThreadLocalRandom.current());
+                // test bug: the following is not guaranteed and not true in JDK8
+                //                assertNotSame(current, threadLocalRandom.get());
+                rand.set(current.nextLong());
+                threadLocalRandom.set(current);
+            }};
+
+        Thread first = newStartedThread(getRandomState);
+        awaitTermination(first);
+        firstRand = rand.get();
+        firstThreadLocalRandom = threadLocalRandom.get();
+
+        for (int i = 0; i < NCALLS; i++) {
+            Thread t = newStartedThread(getRandomState);
+            awaitTermination(t);
+            if (firstRand != rand.get())
+                return;
+        }
+        fail("all threads generate the same pseudo-random sequence");
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ThreadLocalTest.java b/jsr166-tests/src/test/java/jsr166/ThreadLocalTest.java
new file mode 100644
index 0000000..885c2b2
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ThreadLocalTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.Semaphore;
+
+public class ThreadLocalTest extends JSR166TestCase {
+
+    static ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {
+            public Integer initialValue() {
+                return one;
+            }
+        };
+
+    static InheritableThreadLocal<Integer> itl =
+        new InheritableThreadLocal<Integer>() {
+            protected Integer initialValue() {
+                return zero;
+            }
+
+            protected Integer childValue(Integer parentValue) {
+                return new Integer(parentValue.intValue() + 1);
+            }
+        };
+
+    /**
+     * remove causes next access to return initial value
+     */
+    public void testRemove() {
+        assertSame(tl.get(), one);
+        tl.set(two);
+        assertSame(tl.get(), two);
+        tl.remove();
+        assertSame(tl.get(), one);
+    }
+
+    /**
+     * remove in InheritableThreadLocal causes next access to return
+     * initial value
+     */
+    public void testRemoveITL() {
+        assertSame(itl.get(), zero);
+        itl.set(two);
+        assertSame(itl.get(), two);
+        itl.remove();
+        assertSame(itl.get(), zero);
+    }
+
+    private class ITLThread extends Thread {
+        final int[] x;
+        ITLThread(int[] array) { x = array; }
+        public void run() {
+            Thread child = null;
+            if (itl.get().intValue() < x.length - 1) {
+                child = new ITLThread(x);
+                child.start();
+            }
+            Thread.yield();
+
+            int threadId = itl.get().intValue();
+            for (int j = 0; j < threadId; j++) {
+                x[threadId]++;
+                Thread.yield();
+            }
+
+            if (child != null) { // Wait for child (if any)
+                try {
+                    child.join();
+                } catch (InterruptedException e) {
+                    threadUnexpectedException(e);
+                }
+            }
+        }
+    }
+
+    /**
+     * InheritableThreadLocal propagates generic values.
+     */
+    public void testGenericITL() throws InterruptedException {
+        final int threadCount = 10;
+        final int x[] = new int[threadCount];
+        Thread progenitor = new ITLThread(x);
+        progenitor.start();
+        progenitor.join();
+        for (int i = 0; i < threadCount; i++) {
+            assertEquals(i, x[i]);
+        }
+    }
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorSubclassTest.java b/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorSubclassTest.java
new file mode 100644
index 0000000..f16f422
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorSubclassTest.java
@@ -0,0 +1,1769 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.*;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.*;
+
+public class ThreadPoolExecutorSubclassTest extends JSR166TestCase {
+
+    static class CustomTask<V> implements RunnableFuture<V> {
+        final Callable<V> callable;
+        final ReentrantLock lock = new ReentrantLock();
+        final Condition cond = lock.newCondition();
+        boolean done;
+        boolean cancelled;
+        V result;
+        Thread thread;
+        Exception exception;
+        CustomTask(Callable<V> c) {
+            if (c == null) throw new NullPointerException();
+            callable = c;
+        }
+        CustomTask(final Runnable r, final V res) {
+            if (r == null) throw new NullPointerException();
+            callable = new Callable<V>() {
+            public V call() throws Exception { r.run(); return res; }};
+        }
+        public boolean isDone() {
+            lock.lock(); try { return done; } finally { lock.unlock() ; }
+        }
+        public boolean isCancelled() {
+            lock.lock(); try { return cancelled; } finally { lock.unlock() ; }
+        }
+        public boolean cancel(boolean mayInterrupt) {
+            lock.lock();
+            try {
+                if (!done) {
+                    cancelled = true;
+                    done = true;
+                    if (mayInterrupt && thread != null)
+                        thread.interrupt();
+                    return true;
+                }
+                return false;
+            }
+            finally { lock.unlock() ; }
+        }
+        public void run() {
+            lock.lock();
+            try {
+                if (done)
+                    return;
+                thread = Thread.currentThread();
+            }
+            finally { lock.unlock() ; }
+            V v = null;
+            Exception e = null;
+            try {
+                v = callable.call();
+            }
+            catch (Exception ex) {
+                e = ex;
+            }
+            lock.lock();
+            try {
+                result = v;
+                exception = e;
+                done = true;
+                thread = null;
+                cond.signalAll();
+            }
+            finally { lock.unlock(); }
+        }
+        public V get() throws InterruptedException, ExecutionException {
+            lock.lock();
+            try {
+                while (!done)
+                    cond.await();
+                if (exception != null)
+                    throw new ExecutionException(exception);
+                return result;
+            }
+            finally { lock.unlock(); }
+        }
+        public V get(long timeout, TimeUnit unit)
+            throws InterruptedException, ExecutionException, TimeoutException {
+            long nanos = unit.toNanos(timeout);
+            lock.lock();
+            try {
+                for (;;) {
+                    if (done) break;
+                    if (nanos < 0)
+                        throw new TimeoutException();
+                    nanos = cond.awaitNanos(nanos);
+                }
+                if (exception != null)
+                    throw new ExecutionException(exception);
+                return result;
+            }
+            finally { lock.unlock(); }
+        }
+    }
+
+    static class CustomTPE extends ThreadPoolExecutor {
+        protected <V> RunnableFuture<V> newTaskFor(Callable<V> c) {
+            return new CustomTask<V>(c);
+        }
+        protected <V> RunnableFuture<V> newTaskFor(Runnable r, V v) {
+            return new CustomTask<V>(r, v);
+        }
+
+        CustomTPE(int corePoolSize,
+                  int maximumPoolSize,
+                  long keepAliveTime,
+                  TimeUnit unit,
+                  BlockingQueue<Runnable> workQueue) {
+            super(corePoolSize, maximumPoolSize, keepAliveTime, unit,
+                  workQueue);
+        }
+        CustomTPE(int corePoolSize,
+                  int maximumPoolSize,
+                  long keepAliveTime,
+                  TimeUnit unit,
+                  BlockingQueue<Runnable> workQueue,
+                  ThreadFactory threadFactory) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
+             threadFactory);
+        }
+
+        CustomTPE(int corePoolSize,
+                  int maximumPoolSize,
+                  long keepAliveTime,
+                  TimeUnit unit,
+                  BlockingQueue<Runnable> workQueue,
+                  RejectedExecutionHandler handler) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
+              handler);
+        }
+        CustomTPE(int corePoolSize,
+                  int maximumPoolSize,
+                  long keepAliveTime,
+                  TimeUnit unit,
+                  BlockingQueue<Runnable> workQueue,
+                  ThreadFactory threadFactory,
+                  RejectedExecutionHandler handler) {
+            super(corePoolSize, maximumPoolSize, keepAliveTime, unit,
+              workQueue, threadFactory, handler);
+        }
+
+        final CountDownLatch beforeCalled = new CountDownLatch(1);
+        final CountDownLatch afterCalled = new CountDownLatch(1);
+        final CountDownLatch terminatedCalled = new CountDownLatch(1);
+
+        public CustomTPE() {
+            super(1, 1, LONG_DELAY_MS, MILLISECONDS, new SynchronousQueue<Runnable>());
+        }
+        protected void beforeExecute(Thread t, Runnable r) {
+            beforeCalled.countDown();
+        }
+        protected void afterExecute(Runnable r, Throwable t) {
+            afterCalled.countDown();
+        }
+        protected void terminated() {
+            terminatedCalled.countDown();
+        }
+
+        public boolean beforeCalled() {
+            return beforeCalled.getCount() == 0;
+        }
+        public boolean afterCalled() {
+            return afterCalled.getCount() == 0;
+        }
+        public boolean terminatedCalled() {
+            return terminatedCalled.getCount() == 0;
+        }
+    }
+
+    static class FailingThreadFactory implements ThreadFactory {
+        int calls = 0;
+        public Thread newThread(Runnable r) {
+            if (++calls > 1) return null;
+            return new Thread(r);
+        }
+    }
+
+    /**
+     * execute successfully executes a runnable
+     */
+    public void testExecute() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch done = new CountDownLatch(1);
+        final Runnable task = new CheckedRunnable() {
+            public void realRun() {
+                done.countDown();
+            }};
+        try {
+            p.execute(task);
+            assertTrue(done.await(SMALL_DELAY_MS, MILLISECONDS));
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getActiveCount increases but doesn't overestimate, when a
+     * thread becomes active
+     */
+    public void testGetActiveCount() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getActiveCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getActiveCount());
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(1, p.getActiveCount());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * prestartCoreThread starts a thread if under corePoolSize, else doesn't
+     */
+    public void testPrestartCoreThread() {
+        ThreadPoolExecutor p = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        assertEquals(0, p.getPoolSize());
+        assertTrue(p.prestartCoreThread());
+        assertEquals(1, p.getPoolSize());
+        assertTrue(p.prestartCoreThread());
+        assertEquals(2, p.getPoolSize());
+        assertFalse(p.prestartCoreThread());
+        assertEquals(2, p.getPoolSize());
+        joinPool(p);
+    }
+
+    /**
+     * prestartAllCoreThreads starts all corePoolSize threads
+     */
+    public void testPrestartAllCoreThreads() {
+        ThreadPoolExecutor p = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        assertEquals(0, p.getPoolSize());
+        p.prestartAllCoreThreads();
+        assertEquals(2, p.getPoolSize());
+        p.prestartAllCoreThreads();
+        assertEquals(2, p.getPoolSize());
+        joinPool(p);
+    }
+
+    /**
+     * getCompletedTaskCount increases, but doesn't overestimate,
+     * when tasks complete
+     */
+    public void testGetCompletedTaskCount() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch threadProceed = new CountDownLatch(1);
+        final CountDownLatch threadDone = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(0, p.getCompletedTaskCount());
+                    threadProceed.await();
+                    threadDone.countDown();
+                }});
+            await(threadStarted);
+            assertEquals(0, p.getCompletedTaskCount());
+            threadProceed.countDown();
+            threadDone.await();
+            long startTime = System.nanoTime();
+            while (p.getCompletedTaskCount() != 1) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    fail("timed out");
+                Thread.yield();
+            }
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getCorePoolSize returns size given in constructor if not otherwise set
+     */
+    public void testGetCorePoolSize() {
+        ThreadPoolExecutor p = new CustomTPE(1, 1, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        assertEquals(1, p.getCorePoolSize());
+        joinPool(p);
+    }
+
+    /**
+     * getKeepAliveTime returns value given in constructor if not otherwise set
+     */
+    public void testGetKeepAliveTime() {
+        ThreadPoolExecutor p = new CustomTPE(2, 2, 1000, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        assertEquals(1, p.getKeepAliveTime(TimeUnit.SECONDS));
+        joinPool(p);
+    }
+
+    /**
+     * getThreadFactory returns factory in constructor if not set
+     */
+    public void testGetThreadFactory() {
+        ThreadFactory tf = new SimpleThreadFactory();
+        ThreadPoolExecutor p = new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10), tf, new NoOpREHandler());
+        assertSame(tf, p.getThreadFactory());
+        joinPool(p);
+    }
+
+    /**
+     * setThreadFactory sets the thread factory returned by getThreadFactory
+     */
+    public void testSetThreadFactory() {
+        ThreadPoolExecutor p = new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        ThreadFactory tf = new SimpleThreadFactory();
+        p.setThreadFactory(tf);
+        assertSame(tf, p.getThreadFactory());
+        joinPool(p);
+    }
+
+    /**
+     * setThreadFactory(null) throws NPE
+     */
+    public void testSetThreadFactoryNull() {
+        ThreadPoolExecutor p = new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            p.setThreadFactory(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getRejectedExecutionHandler returns handler in constructor if not set
+     */
+    public void testGetRejectedExecutionHandler() {
+        RejectedExecutionHandler h = new NoOpREHandler();
+        ThreadPoolExecutor p = new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10), h);
+        assertSame(h, p.getRejectedExecutionHandler());
+        joinPool(p);
+    }
+
+    /**
+     * setRejectedExecutionHandler sets the handler returned by
+     * getRejectedExecutionHandler
+     */
+    public void testSetRejectedExecutionHandler() {
+        ThreadPoolExecutor p = new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        RejectedExecutionHandler h = new NoOpREHandler();
+        p.setRejectedExecutionHandler(h);
+        assertSame(h, p.getRejectedExecutionHandler());
+        joinPool(p);
+    }
+
+    /**
+     * setRejectedExecutionHandler(null) throws NPE
+     */
+    public void testSetRejectedExecutionHandlerNull() {
+        ThreadPoolExecutor p = new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            p.setRejectedExecutionHandler(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getLargestPoolSize increases, but doesn't overestimate, when
+     * multiple threads active
+     */
+    public void testGetLargestPoolSize() throws InterruptedException {
+        final int THREADS = 3;
+        final ThreadPoolExecutor p =
+            new CustomTPE(THREADS, THREADS,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getLargestPoolSize());
+            for (int i = 0; i < THREADS; i++)
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadsStarted.countDown();
+                        done.await();
+                        assertEquals(THREADS, p.getLargestPoolSize());
+                    }});
+            assertTrue(threadsStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(THREADS, p.getLargestPoolSize());
+        } finally {
+            done.countDown();
+            joinPool(p);
+            assertEquals(THREADS, p.getLargestPoolSize());
+        }
+    }
+
+    /**
+     * getMaximumPoolSize returns value given in constructor if not
+     * otherwise set
+     */
+    public void testGetMaximumPoolSize() {
+        ThreadPoolExecutor p = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        assertEquals(2, p.getMaximumPoolSize());
+        joinPool(p);
+    }
+
+    /**
+     * getPoolSize increases, but doesn't overestimate, when threads
+     * become active
+     */
+    public void testGetPoolSize() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getPoolSize());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(1, p.getPoolSize());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getTaskCount increases, but doesn't overestimate, when tasks submitted
+     */
+    public void testGetTaskCount() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getTaskCount());
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(1, p.getTaskCount());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * isShutdown is false before shutdown, true after
+     */
+    public void testIsShutdown() {
+
+        ThreadPoolExecutor p = new CustomTPE(1, 1, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        assertFalse(p.isShutdown());
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        assertTrue(p.isShutdown());
+        joinPool(p);
+    }
+
+    /**
+     * isTerminated is false before termination, true after
+     */
+    public void testIsTerminated() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertFalse(p.isTerminating());
+            done.countDown();
+        } finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertFalse(p.isTerminating());
+    }
+
+    /**
+     * isTerminating is not true when running or when terminated
+     */
+    public void testIsTerminating() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertFalse(p.isTerminating());
+            done.countDown();
+        } finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertFalse(p.isTerminating());
+    }
+
+    /**
+     * getQueue returns the work queue, which contains queued tasks
+     */
+    public void testGetQueue() throws InterruptedException {
+        final BlockingQueue<Runnable> q = new ArrayBlockingQueue<Runnable>(10);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          q);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            FutureTask[] tasks = new FutureTask[5];
+            for (int i = 0; i < tasks.length; i++) {
+                Callable task = new CheckedCallable<Boolean>() {
+                    public Boolean realCall() throws InterruptedException {
+                        threadStarted.countDown();
+                        assertSame(q, p.getQueue());
+                        done.await();
+                        return Boolean.TRUE;
+                    }};
+                tasks[i] = new FutureTask(task);
+                p.execute(tasks[i]);
+            }
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertSame(q, p.getQueue());
+            assertFalse(q.contains(tasks[0]));
+            assertTrue(q.contains(tasks[tasks.length - 1]));
+            assertEquals(tasks.length - 1, q.size());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * remove(task) removes queued task, and fails to remove active task
+     */
+    public void testRemove() throws InterruptedException {
+        BlockingQueue<Runnable> q = new ArrayBlockingQueue<Runnable>(10);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          q);
+        Runnable[] tasks = new Runnable[6];
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            for (int i = 0; i < tasks.length; i++) {
+                tasks[i] = new CheckedRunnable() {
+                        public void realRun() throws InterruptedException {
+                            threadStarted.countDown();
+                            done.await();
+                        }};
+                p.execute(tasks[i]);
+            }
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertFalse(p.remove(tasks[0]));
+            assertTrue(q.contains(tasks[4]));
+            assertTrue(q.contains(tasks[3]));
+            assertTrue(p.remove(tasks[4]));
+            assertFalse(p.remove(tasks[4]));
+            assertFalse(q.contains(tasks[4]));
+            assertTrue(q.contains(tasks[3]));
+            assertTrue(p.remove(tasks[3]));
+            assertFalse(q.contains(tasks[3]));
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * purge removes cancelled tasks from the queue
+     */
+    public void testPurge() throws InterruptedException {
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        final BlockingQueue<Runnable> q = new ArrayBlockingQueue<Runnable>(10);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          q);
+        FutureTask[] tasks = new FutureTask[5];
+        try {
+            for (int i = 0; i < tasks.length; i++) {
+                Callable task = new CheckedCallable<Boolean>() {
+                    public Boolean realCall() throws InterruptedException {
+                        threadStarted.countDown();
+                        done.await();
+                        return Boolean.TRUE;
+                    }};
+                tasks[i] = new FutureTask(task);
+                p.execute(tasks[i]);
+            }
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(tasks.length, p.getTaskCount());
+            assertEquals(tasks.length - 1, q.size());
+            assertEquals(1L, p.getActiveCount());
+            assertEquals(0L, p.getCompletedTaskCount());
+            tasks[4].cancel(true);
+            tasks[3].cancel(false);
+            p.purge();
+            assertEquals(tasks.length - 3, q.size());
+            assertEquals(tasks.length - 2, p.getTaskCount());
+            p.purge();         // Nothing to do
+            assertEquals(tasks.length - 3, q.size());
+            assertEquals(tasks.length - 2, p.getTaskCount());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * shutdownNow returns a list containing tasks that were not run
+     */
+    public void testShutdownNow() {
+        ThreadPoolExecutor p = new CustomTPE(1, 1, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        List l;
+        try {
+            for (int i = 0; i < 5; i++)
+                p.execute(new MediumPossiblyInterruptedRunnable());
+        }
+        finally {
+            try {
+                l = p.shutdownNow();
+            } catch (SecurityException ok) { return; }
+        }
+        assertTrue(p.isShutdown());
+        assertTrue(l.size() <= 4);
+    }
+
+    // Exception Tests
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor1() {
+        try {
+            new CustomTPE(-1,1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor2() {
+        try {
+            new CustomTPE(1,-1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor3() {
+        try {
+            new CustomTPE(1,0,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor4() {
+        try {
+            new CustomTPE(1,2,-1L,MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor5() {
+        try {
+            new CustomTPE(2,1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException() {
+        try {
+            new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor6() {
+        try {
+            new CustomTPE(-1,1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor7() {
+        try {
+            new CustomTPE(1,-1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor8() {
+        try {
+            new CustomTPE(1,0,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor9() {
+        try {
+            new CustomTPE(1,2,-1L,MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor10() {
+        try {
+            new CustomTPE(2,1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException2() {
+        try {
+            new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,null,new SimpleThreadFactory());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if threadFactory is set to null
+     */
+    public void testConstructorNullPointerException3() {
+        try {
+            ThreadFactory f = null;
+            new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(10),f);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor11() {
+        try {
+            new CustomTPE(-1,1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor12() {
+        try {
+            new CustomTPE(1,-1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor13() {
+        try {
+            new CustomTPE(1,0,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor14() {
+        try {
+            new CustomTPE(1,2,-1L,MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor15() {
+        try {
+            new CustomTPE(2,1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException4() {
+        try {
+            new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,null,new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if handler is set to null
+     */
+    public void testConstructorNullPointerException5() {
+        try {
+            RejectedExecutionHandler r = null;
+            new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(10),r);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor16() {
+        try {
+            new CustomTPE(-1,1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory(),new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor17() {
+        try {
+            new CustomTPE(1,-1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory(),new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor18() {
+        try {
+            new CustomTPE(1,0,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory(),new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor19() {
+        try {
+            new CustomTPE(1,2,-1L,MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory(),new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor20() {
+        try {
+            new CustomTPE(2,1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory(),new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is null
+     */
+    public void testConstructorNullPointerException6() {
+        try {
+            new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,null,new SimpleThreadFactory(),new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if handler is null
+     */
+    public void testConstructorNullPointerException7() {
+        try {
+            RejectedExecutionHandler r = null;
+            new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory(),r);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if ThreadFactory is null
+     */
+    public void testConstructorNullPointerException8() {
+        try {
+            new CustomTPE(1, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          (ThreadFactory) null,
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * execute throws RejectedExecutionException if saturated.
+     */
+    public void testSaturatedExecute() {
+        ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1));
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    done.await();
+                }};
+            for (int i = 0; i < 2; ++i)
+                p.execute(task);
+            for (int i = 0; i < 2; ++i) {
+                try {
+                    p.execute(task);
+                    shouldThrow();
+                } catch (RejectedExecutionException success) {}
+                assertTrue(p.getTaskCount() <= 2);
+            }
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * executor using CallerRunsPolicy runs task if saturated.
+     */
+    public void testSaturatedExecute2() {
+        RejectedExecutionHandler h = new CustomTPE.CallerRunsPolicy();
+        ThreadPoolExecutor p = new CustomTPE(1, 1,
+                                             LONG_DELAY_MS, MILLISECONDS,
+                                             new ArrayBlockingQueue<Runnable>(1),
+                                             h);
+        try {
+            TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
+            for (int i = 0; i < tasks.length; ++i)
+                tasks[i] = new TrackedNoOpRunnable();
+            TrackedLongRunnable mr = new TrackedLongRunnable();
+            p.execute(mr);
+            for (int i = 0; i < tasks.length; ++i)
+                p.execute(tasks[i]);
+            for (int i = 1; i < tasks.length; ++i)
+                assertTrue(tasks[i].done);
+            try { p.shutdownNow(); } catch (SecurityException ok) { return; }
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * executor using DiscardPolicy drops task if saturated.
+     */
+    public void testSaturatedExecute3() {
+        RejectedExecutionHandler h = new CustomTPE.DiscardPolicy();
+        ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1),
+                          h);
+        try {
+            TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
+            for (int i = 0; i < tasks.length; ++i)
+                tasks[i] = new TrackedNoOpRunnable();
+            p.execute(new TrackedLongRunnable());
+            for (TrackedNoOpRunnable task : tasks)
+                p.execute(task);
+            for (TrackedNoOpRunnable task : tasks)
+                assertFalse(task.done);
+            try { p.shutdownNow(); } catch (SecurityException ok) { return; }
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * executor using DiscardOldestPolicy drops oldest task if saturated.
+     */
+    public void testSaturatedExecute4() {
+        RejectedExecutionHandler h = new CustomTPE.DiscardOldestPolicy();
+        ThreadPoolExecutor p = new CustomTPE(1,1, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(1), h);
+        try {
+            p.execute(new TrackedLongRunnable());
+            TrackedLongRunnable r2 = new TrackedLongRunnable();
+            p.execute(r2);
+            assertTrue(p.getQueue().contains(r2));
+            TrackedNoOpRunnable r3 = new TrackedNoOpRunnable();
+            p.execute(r3);
+            assertFalse(p.getQueue().contains(r2));
+            assertTrue(p.getQueue().contains(r3));
+            try { p.shutdownNow(); } catch (SecurityException ok) { return; }
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * execute throws RejectedExecutionException if shutdown
+     */
+    public void testRejectedExecutionExceptionOnShutdown() {
+        ThreadPoolExecutor p =
+            new CustomTPE(1,1,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(1));
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try {
+            p.execute(new NoOpRunnable());
+            shouldThrow();
+        } catch (RejectedExecutionException success) {}
+
+        joinPool(p);
+    }
+
+    /**
+     * execute using CallerRunsPolicy drops task on shutdown
+     */
+    public void testCallerRunsOnShutdown() {
+        RejectedExecutionHandler h = new CustomTPE.CallerRunsPolicy();
+        ThreadPoolExecutor p = new CustomTPE(1,1, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(1), h);
+
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * execute using DiscardPolicy drops task on shutdown
+     */
+    public void testDiscardOnShutdown() {
+        RejectedExecutionHandler h = new CustomTPE.DiscardPolicy();
+        ThreadPoolExecutor p = new CustomTPE(1,1, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(1), h);
+
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * execute using DiscardOldestPolicy drops task on shutdown
+     */
+    public void testDiscardOldestOnShutdown() {
+        RejectedExecutionHandler h = new CustomTPE.DiscardOldestPolicy();
+        ThreadPoolExecutor p = new CustomTPE(1,1, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(1), h);
+
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * execute(null) throws NPE
+     */
+    public void testExecuteNull() {
+        ThreadPoolExecutor p = null;
+        try {
+            p = new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));
+            p.execute(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+
+        joinPool(p);
+    }
+
+    /**
+     * setCorePoolSize of negative value throws IllegalArgumentException
+     */
+    public void testCorePoolSizeIllegalArgumentException() {
+        ThreadPoolExecutor p =
+            new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));
+        try {
+            p.setCorePoolSize(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        joinPool(p);
+    }
+
+    /**
+     * setMaximumPoolSize(int) throws IllegalArgumentException
+     * if given a value less the core pool size
+     */
+    public void testMaximumPoolSizeIllegalArgumentException() {
+        ThreadPoolExecutor p =
+            new CustomTPE(2,3,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));
+        try {
+            p.setMaximumPoolSize(1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        joinPool(p);
+    }
+
+    /**
+     * setMaximumPoolSize throws IllegalArgumentException
+     * if given a negative value
+     */
+    public void testMaximumPoolSizeIllegalArgumentException2() {
+        ThreadPoolExecutor p =
+            new CustomTPE(2,3,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));
+        try {
+            p.setMaximumPoolSize(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        joinPool(p);
+    }
+
+    /**
+     * setKeepAliveTime throws IllegalArgumentException
+     * when given a negative value
+     */
+    public void testKeepAliveTimeIllegalArgumentException() {
+        ThreadPoolExecutor p =
+            new CustomTPE(2,3,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));
+
+        try {
+            p.setKeepAliveTime(-1,MILLISECONDS);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        joinPool(p);
+    }
+
+    /**
+     * terminated() is called on termination
+     */
+    public void testTerminated() {
+        CustomTPE p = new CustomTPE();
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        assertTrue(p.terminatedCalled());
+        joinPool(p);
+    }
+
+    /**
+     * beforeExecute and afterExecute are called when executing task
+     */
+    public void testBeforeAfter() throws InterruptedException {
+        CustomTPE p = new CustomTPE();
+        try {
+            final CountDownLatch done = new CountDownLatch(1);
+            final CheckedRunnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                }};
+            p.execute(task);
+            await(p.afterCalled);
+            assertEquals(0, done.getCount());
+            assertTrue(p.afterCalled());
+            assertTrue(p.beforeCalled());
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * completed submit of callable returns result
+     */
+    public void testSubmitCallable() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            Future<String> future = e.submit(new StringTask());
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * completed submit of runnable returns successfully
+     */
+    public void testSubmitRunnable() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            Future<?> future = e.submit(new NoOpRunnable());
+            future.get();
+            assertTrue(future.isDone());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * completed submit of (runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NPE
+     */
+    public void testInvokeAny1() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            e.invokeAny(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IAE
+     */
+    public void testInvokeAny2() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            e.invokeAny(new ArrayList<Callable<String>>());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NPE if c has null elements
+     */
+    public void testInvokeAny3() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(latchAwaitingStringTask(latch));
+        l.add(null);
+        try {
+            e.invokeAny(l);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            latch.countDown();
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testInvokeAny4() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        try {
+            e.invokeAny(l);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task
+     */
+    public void testInvokeAny5() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NPE
+     */
+    public void testInvokeAll1() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            e.invokeAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NPE if c has null elements
+     */
+    public void testInvokeAll3() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        l.add(null);
+        try {
+            e.invokeAll(l);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testInvokeAll4() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        List<Future<String>> futures = e.invokeAll(l);
+        assertEquals(1, futures.size());
+        try {
+            futures.get(0).get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks
+     */
+    public void testInvokeAll5() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NPE
+     */
+    public void testTimedInvokeAny1() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(,,null) throws NPE
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IAE
+     */
+    public void testTimedInvokeAny2() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(latchAwaitingStringTask(latch));
+        l.add(null);
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            latch.countDown();
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task
+     */
+    public void testTimedInvokeAny5() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NPE
+     */
+    public void testTimedInvokeAll1() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(,,null) throws NPE
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        try {
+            e.invokeAll(l, MEDIUM_DELAY_MS, null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAll3() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        l.add(null);
+        try {
+            e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        List<Future<String>> futures =
+            e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+        assertEquals(1, futures.size());
+        try {
+            futures.get(0).get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks
+     */
+    public void testTimedInvokeAll5() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(c) cancels tasks not completed by timeout
+     */
+    public void testTimedInvokeAll6() throws Exception {
+        ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(Executors.callable(new MediumPossiblyInterruptedRunnable(), TEST_STRING));
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, SHORT_DELAY_MS, MILLISECONDS);
+            assertEquals(l.size(), futures.size());
+            for (Future future : futures)
+                assertTrue(future.isDone());
+            assertFalse(futures.get(0).isCancelled());
+            assertTrue(futures.get(1).isCancelled());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * Execution continues if there is at least one thread even if
+     * thread factory fails to create more
+     */
+    public void testFailingThreadFactory() throws InterruptedException {
+        final ExecutorService e =
+            new CustomTPE(100, 100,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new LinkedBlockingQueue<Runnable>(),
+                          new FailingThreadFactory());
+        try {
+            final int TASKS = 100;
+            final CountDownLatch done = new CountDownLatch(TASKS);
+            for (int k = 0; k < TASKS; ++k)
+                e.execute(new CheckedRunnable() {
+                    public void realRun() {
+                        done.countDown();
+                    }});
+            assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * allowsCoreThreadTimeOut is by default false.
+     */
+    public void testAllowsCoreThreadTimeOut() {
+        ThreadPoolExecutor p = new CustomTPE(2, 2, 1000, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+        assertFalse(p.allowsCoreThreadTimeOut());
+        joinPool(p);
+    }
+
+    /**
+     * allowCoreThreadTimeOut(true) causes idle threads to time out
+     */
+    public void testAllowCoreThreadTimeOut_true() throws Exception {
+        long coreThreadTimeOut = SHORT_DELAY_MS;
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 10,
+                          coreThreadTimeOut, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        try {
+            p.allowCoreThreadTimeOut(true);
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                }});
+            await(threadStarted);
+            delay(coreThreadTimeOut);
+            long startTime = System.nanoTime();
+            while (p.getPoolSize() > 0
+                   && millisElapsedSince(startTime) < LONG_DELAY_MS)
+                Thread.yield();
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            assertEquals(0, p.getPoolSize());
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * allowCoreThreadTimeOut(false) causes idle threads not to time out
+     */
+    public void testAllowCoreThreadTimeOut_false() throws Exception {
+        long coreThreadTimeOut = SHORT_DELAY_MS;
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 10,
+                          coreThreadTimeOut, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        try {
+            p.allowCoreThreadTimeOut(false);
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertTrue(p.getPoolSize() >= 1);
+                }});
+            delay(2 * coreThreadTimeOut);
+            assertTrue(p.getPoolSize() >= 1);
+        } finally {
+            joinPool(p);
+        }
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorTest.java b/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorTest.java
new file mode 100644
index 0000000..55f769b
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorTest.java
@@ -0,0 +1,2010 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.*;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import java.util.*;
+
+public class ThreadPoolExecutorTest extends JSR166TestCase {
+
+    static class ExtendedTPE extends ThreadPoolExecutor {
+        final CountDownLatch beforeCalled = new CountDownLatch(1);
+        final CountDownLatch afterCalled = new CountDownLatch(1);
+        final CountDownLatch terminatedCalled = new CountDownLatch(1);
+
+        public ExtendedTPE() {
+            super(1, 1, LONG_DELAY_MS, MILLISECONDS, new SynchronousQueue<Runnable>());
+        }
+        protected void beforeExecute(Thread t, Runnable r) {
+            beforeCalled.countDown();
+        }
+        protected void afterExecute(Runnable r, Throwable t) {
+            afterCalled.countDown();
+        }
+        protected void terminated() {
+            terminatedCalled.countDown();
+        }
+
+        public boolean beforeCalled() {
+            return beforeCalled.getCount() == 0;
+        }
+        public boolean afterCalled() {
+            return afterCalled.getCount() == 0;
+        }
+        public boolean terminatedCalled() {
+            return terminatedCalled.getCount() == 0;
+        }
+    }
+
+    static class FailingThreadFactory implements ThreadFactory {
+        int calls = 0;
+        public Thread newThread(Runnable r) {
+            if (++calls > 1) return null;
+            return new Thread(r);
+        }
+    }
+
+    /**
+     * execute successfully executes a runnable
+     */
+    public void testExecute() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch done = new CountDownLatch(1);
+        final Runnable task = new CheckedRunnable() {
+            public void realRun() {
+                done.countDown();
+            }};
+        try {
+            p.execute(task);
+            assertTrue(done.await(SMALL_DELAY_MS, MILLISECONDS));
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getActiveCount increases but doesn't overestimate, when a
+     * thread becomes active
+     */
+    public void testGetActiveCount() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getActiveCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getActiveCount());
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(1, p.getActiveCount());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * prestartCoreThread starts a thread if under corePoolSize, else doesn't
+     */
+    public void testPrestartCoreThread() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        assertEquals(0, p.getPoolSize());
+        assertTrue(p.prestartCoreThread());
+        assertEquals(1, p.getPoolSize());
+        assertTrue(p.prestartCoreThread());
+        assertEquals(2, p.getPoolSize());
+        assertFalse(p.prestartCoreThread());
+        assertEquals(2, p.getPoolSize());
+        joinPool(p);
+    }
+
+    /**
+     * prestartAllCoreThreads starts all corePoolSize threads
+     */
+    public void testPrestartAllCoreThreads() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        assertEquals(0, p.getPoolSize());
+        p.prestartAllCoreThreads();
+        assertEquals(2, p.getPoolSize());
+        p.prestartAllCoreThreads();
+        assertEquals(2, p.getPoolSize());
+        joinPool(p);
+    }
+
+    /**
+     * getCompletedTaskCount increases, but doesn't overestimate,
+     * when tasks complete
+     */
+    public void testGetCompletedTaskCount() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch threadProceed = new CountDownLatch(1);
+        final CountDownLatch threadDone = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(0, p.getCompletedTaskCount());
+                    threadProceed.await();
+                    threadDone.countDown();
+                }});
+            await(threadStarted);
+            assertEquals(0, p.getCompletedTaskCount());
+            threadProceed.countDown();
+            threadDone.await();
+            long startTime = System.nanoTime();
+            while (p.getCompletedTaskCount() != 1) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    fail("timed out");
+                Thread.yield();
+            }
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getCorePoolSize returns size given in constructor if not otherwise set
+     */
+    public void testGetCorePoolSize() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        assertEquals(1, p.getCorePoolSize());
+        joinPool(p);
+    }
+
+    /**
+     * getKeepAliveTime returns value given in constructor if not otherwise set
+     */
+    public void testGetKeepAliveTime() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 2,
+                                   1000, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        assertEquals(1, p.getKeepAliveTime(TimeUnit.SECONDS));
+        joinPool(p);
+    }
+
+    /**
+     * getThreadFactory returns factory in constructor if not set
+     */
+    public void testGetThreadFactory() {
+        ThreadFactory tf = new SimpleThreadFactory();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   tf,
+                                   new NoOpREHandler());
+        assertSame(tf, p.getThreadFactory());
+        joinPool(p);
+    }
+
+    /**
+     * setThreadFactory sets the thread factory returned by getThreadFactory
+     */
+    public void testSetThreadFactory() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        ThreadFactory tf = new SimpleThreadFactory();
+        p.setThreadFactory(tf);
+        assertSame(tf, p.getThreadFactory());
+        joinPool(p);
+    }
+
+    /**
+     * setThreadFactory(null) throws NPE
+     */
+    public void testSetThreadFactoryNull() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            p.setThreadFactory(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getRejectedExecutionHandler returns handler in constructor if not set
+     */
+    public void testGetRejectedExecutionHandler() {
+        final RejectedExecutionHandler h = new NoOpREHandler();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   h);
+        assertSame(h, p.getRejectedExecutionHandler());
+        joinPool(p);
+    }
+
+    /**
+     * setRejectedExecutionHandler sets the handler returned by
+     * getRejectedExecutionHandler
+     */
+    public void testSetRejectedExecutionHandler() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        RejectedExecutionHandler h = new NoOpREHandler();
+        p.setRejectedExecutionHandler(h);
+        assertSame(h, p.getRejectedExecutionHandler());
+        joinPool(p);
+    }
+
+    /**
+     * setRejectedExecutionHandler(null) throws NPE
+     */
+    public void testSetRejectedExecutionHandlerNull() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            p.setRejectedExecutionHandler(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getLargestPoolSize increases, but doesn't overestimate, when
+     * multiple threads active
+     */
+    public void testGetLargestPoolSize() throws InterruptedException {
+        final int THREADS = 3;
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(THREADS, THREADS,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getLargestPoolSize());
+            for (int i = 0; i < THREADS; i++)
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadsStarted.countDown();
+                        done.await();
+                        assertEquals(THREADS, p.getLargestPoolSize());
+                    }});
+            assertTrue(threadsStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(THREADS, p.getLargestPoolSize());
+        } finally {
+            done.countDown();
+            joinPool(p);
+            assertEquals(THREADS, p.getLargestPoolSize());
+        }
+    }
+
+    /**
+     * getMaximumPoolSize returns value given in constructor if not
+     * otherwise set
+     */
+    public void testGetMaximumPoolSize() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 3,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        assertEquals(3, p.getMaximumPoolSize());
+        joinPool(p);
+    }
+
+    /**
+     * getPoolSize increases, but doesn't overestimate, when threads
+     * become active
+     */
+    public void testGetPoolSize() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getPoolSize());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(1, p.getPoolSize());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * getTaskCount increases, but doesn't overestimate, when tasks submitted
+     */
+    public void testGetTaskCount() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertEquals(0, p.getTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getTaskCount());
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(1, p.getTaskCount());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * isShutdown is false before shutdown, true after
+     */
+    public void testIsShutdown() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        assertFalse(p.isShutdown());
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        assertTrue(p.isShutdown());
+        joinPool(p);
+    }
+
+    /**
+     * awaitTermination on a non-shutdown pool times out
+     */
+    public void testAwaitTermination_timesOut() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        assertFalse(p.isTerminated());
+        assertFalse(p.awaitTermination(Long.MIN_VALUE, NANOSECONDS));
+        assertFalse(p.awaitTermination(Long.MIN_VALUE, MILLISECONDS));
+        assertFalse(p.awaitTermination(-1L, NANOSECONDS));
+        assertFalse(p.awaitTermination(-1L, MILLISECONDS));
+        assertFalse(p.awaitTermination(0L, NANOSECONDS));
+        assertFalse(p.awaitTermination(0L, MILLISECONDS));
+        long timeoutNanos = 999999L;
+        long startTime = System.nanoTime();
+        assertFalse(p.awaitTermination(timeoutNanos, NANOSECONDS));
+        assertTrue(System.nanoTime() - startTime >= timeoutNanos);
+        assertFalse(p.isTerminated());
+        startTime = System.nanoTime();
+        long timeoutMillis = timeoutMillis();
+        assertFalse(p.awaitTermination(timeoutMillis, MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+        assertFalse(p.isTerminated());
+        p.shutdown();
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+    }
+
+    /**
+     * isTerminated is false before termination, true after
+     */
+    public void testIsTerminated() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        assertFalse(p.isTerminated());
+        try {
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminated());
+                    threadStarted.countDown();
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertFalse(p.isTerminating());
+            done.countDown();
+        } finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+    }
+
+    /**
+     * isTerminating is not true when running or when terminated
+     */
+    public void testIsTerminating() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    done.await();
+                }});
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertFalse(p.isTerminating());
+            done.countDown();
+        } finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertFalse(p.isTerminating());
+    }
+
+    /**
+     * getQueue returns the work queue, which contains queued tasks
+     */
+    public void testGetQueue() throws InterruptedException {
+        final BlockingQueue<Runnable> q = new ArrayBlockingQueue<Runnable>(10);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   q);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            FutureTask[] tasks = new FutureTask[5];
+            for (int i = 0; i < tasks.length; i++) {
+                Callable task = new CheckedCallable<Boolean>() {
+                    public Boolean realCall() throws InterruptedException {
+                        threadStarted.countDown();
+                        assertSame(q, p.getQueue());
+                        done.await();
+                        return Boolean.TRUE;
+                    }};
+                tasks[i] = new FutureTask(task);
+                p.execute(tasks[i]);
+            }
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertSame(q, p.getQueue());
+            assertFalse(q.contains(tasks[0]));
+            assertTrue(q.contains(tasks[tasks.length - 1]));
+            assertEquals(tasks.length - 1, q.size());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * remove(task) removes queued task, and fails to remove active task
+     */
+    public void testRemove() throws InterruptedException {
+        BlockingQueue<Runnable> q = new ArrayBlockingQueue<Runnable>(10);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   q);
+        Runnable[] tasks = new Runnable[5];
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            for (int i = 0; i < tasks.length; i++) {
+                tasks[i] = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        done.await();
+                    }};
+                p.execute(tasks[i]);
+            }
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertFalse(p.remove(tasks[0]));
+            assertTrue(q.contains(tasks[4]));
+            assertTrue(q.contains(tasks[3]));
+            assertTrue(p.remove(tasks[4]));
+            assertFalse(p.remove(tasks[4]));
+            assertFalse(q.contains(tasks[4]));
+            assertTrue(q.contains(tasks[3]));
+            assertTrue(p.remove(tasks[3]));
+            assertFalse(q.contains(tasks[3]));
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * purge removes cancelled tasks from the queue
+     */
+    public void testPurge() throws InterruptedException {
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        final BlockingQueue<Runnable> q = new ArrayBlockingQueue<Runnable>(10);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   q);
+        FutureTask[] tasks = new FutureTask[5];
+        try {
+            for (int i = 0; i < tasks.length; i++) {
+                Callable task = new CheckedCallable<Boolean>() {
+                    public Boolean realCall() throws InterruptedException {
+                        threadStarted.countDown();
+                        done.await();
+                        return Boolean.TRUE;
+                    }};
+                tasks[i] = new FutureTask(task);
+                p.execute(tasks[i]);
+            }
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            assertEquals(tasks.length, p.getTaskCount());
+            assertEquals(tasks.length - 1, q.size());
+            assertEquals(1L, p.getActiveCount());
+            assertEquals(0L, p.getCompletedTaskCount());
+            tasks[4].cancel(true);
+            tasks[3].cancel(false);
+            p.purge();
+            assertEquals(tasks.length - 3, q.size());
+            assertEquals(tasks.length - 2, p.getTaskCount());
+            p.purge();         // Nothing to do
+            assertEquals(tasks.length - 3, q.size());
+            assertEquals(tasks.length - 2, p.getTaskCount());
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * shutdownNow returns a list containing tasks that were not run
+     */
+    public void testShutdownNow() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        List l;
+        try {
+            for (int i = 0; i < 5; i++)
+                p.execute(new MediumPossiblyInterruptedRunnable());
+        }
+        finally {
+            try {
+                l = p.shutdownNow();
+            } catch (SecurityException ok) { return; }
+        }
+        assertTrue(p.isShutdown());
+        assertTrue(l.size() <= 4);
+    }
+
+    // Exception Tests
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor1() {
+        try {
+            new ThreadPoolExecutor(-1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor2() {
+        try {
+            new ThreadPoolExecutor(1, -1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor3() {
+        try {
+            new ThreadPoolExecutor(1, 0,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor4() {
+        try {
+            new ThreadPoolExecutor(1, 2,
+                                   -1L, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor5() {
+        try {
+            new ThreadPoolExecutor(2, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException() {
+        try {
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   (BlockingQueue) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor6() {
+        try {
+            new ThreadPoolExecutor(-1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor7() {
+        try {
+            new ThreadPoolExecutor(1, -1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor8() {
+        try {
+            new ThreadPoolExecutor(1, 0,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor9() {
+        try {
+            new ThreadPoolExecutor(1, 2,
+                                   -1L, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor10() {
+        try {
+            new ThreadPoolExecutor(2, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException2() {
+        try {
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   (BlockingQueue) null,
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if threadFactory is set to null
+     */
+    public void testConstructorNullPointerException3() {
+        try {
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   (ThreadFactory) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor11() {
+        try {
+            new ThreadPoolExecutor(-1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor12() {
+        try {
+            new ThreadPoolExecutor(1, -1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor13() {
+        try {
+            new ThreadPoolExecutor(1, 0,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor14() {
+        try {
+            new ThreadPoolExecutor(1, 2,
+                                   -1L, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor15() {
+        try {
+            new ThreadPoolExecutor(2, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException4() {
+        try {
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   (BlockingQueue) null,
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if handler is set to null
+     */
+    public void testConstructorNullPointerException5() {
+        try {
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   (RejectedExecutionHandler) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor16() {
+        try {
+            new ThreadPoolExecutor(-1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor17() {
+        try {
+            new ThreadPoolExecutor(1, -1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor18() {
+        try {
+            new ThreadPoolExecutor(1, 0,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor19() {
+        try {
+            new ThreadPoolExecutor(1, 2,
+                                   -1L, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor20() {
+        try {
+            new ThreadPoolExecutor(2, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is null
+     */
+    public void testConstructorNullPointerException6() {
+        try {
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   (BlockingQueue) null,
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if handler is null
+     */
+    public void testConstructorNullPointerException7() {
+        try {
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   (RejectedExecutionHandler) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if ThreadFactory is null
+     */
+    public void testConstructorNullPointerException8() {
+        try {
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   (ThreadFactory) null,
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * get of submitted callable throws InterruptedException if interrupted
+     */
+    public void testInterruptedSubmit() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   60, TimeUnit.SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+                public void realRun() throws Exception {
+                    Callable task = new CheckedCallable<Boolean>() {
+                        public Boolean realCall() throws InterruptedException {
+                            threadStarted.countDown();
+                            done.await();
+                            return Boolean.TRUE;
+                        }};
+                    p.submit(task).get();
+                }});
+
+            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+            t.interrupt();
+            awaitTermination(t, MEDIUM_DELAY_MS);
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * execute throws RejectedExecutionException if saturated.
+     */
+    public void testSaturatedExecute() {
+        ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1));
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    done.await();
+                }};
+            for (int i = 0; i < 2; ++i)
+                p.execute(task);
+            for (int i = 0; i < 2; ++i) {
+                try {
+                    p.execute(task);
+                    shouldThrow();
+                } catch (RejectedExecutionException success) {}
+                assertTrue(p.getTaskCount() <= 2);
+            }
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * submit(runnable) throws RejectedExecutionException if saturated.
+     */
+    public void testSaturatedSubmitRunnable() {
+        ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1));
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    done.await();
+                }};
+            for (int i = 0; i < 2; ++i)
+                p.submit(task);
+            for (int i = 0; i < 2; ++i) {
+                try {
+                    p.execute(task);
+                    shouldThrow();
+                } catch (RejectedExecutionException success) {}
+                assertTrue(p.getTaskCount() <= 2);
+            }
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * submit(callable) throws RejectedExecutionException if saturated.
+     */
+    public void testSaturatedSubmitCallable() {
+        ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1));
+        final CountDownLatch done = new CountDownLatch(1);
+        try {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    done.await();
+                }};
+            for (int i = 0; i < 2; ++i)
+                p.submit(Executors.callable(task));
+            for (int i = 0; i < 2; ++i) {
+                try {
+                    p.execute(task);
+                    shouldThrow();
+                } catch (RejectedExecutionException success) {}
+                assertTrue(p.getTaskCount() <= 2);
+            }
+        } finally {
+            done.countDown();
+            joinPool(p);
+        }
+    }
+
+    /**
+     * executor using CallerRunsPolicy runs task if saturated.
+     */
+    public void testSaturatedExecute2() {
+        RejectedExecutionHandler h = new ThreadPoolExecutor.CallerRunsPolicy();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS,
+                                   MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1),
+                                   h);
+        try {
+            TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
+            for (int i = 0; i < tasks.length; ++i)
+                tasks[i] = new TrackedNoOpRunnable();
+            TrackedLongRunnable mr = new TrackedLongRunnable();
+            p.execute(mr);
+            for (int i = 0; i < tasks.length; ++i)
+                p.execute(tasks[i]);
+            for (int i = 1; i < tasks.length; ++i)
+                assertTrue(tasks[i].done);
+            try { p.shutdownNow(); } catch (SecurityException ok) { return; }
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * executor using DiscardPolicy drops task if saturated.
+     */
+    public void testSaturatedExecute3() {
+        RejectedExecutionHandler h = new ThreadPoolExecutor.DiscardPolicy();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1),
+                                   h);
+        try {
+            TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
+            for (int i = 0; i < tasks.length; ++i)
+                tasks[i] = new TrackedNoOpRunnable();
+            p.execute(new TrackedLongRunnable());
+            for (TrackedNoOpRunnable task : tasks)
+                p.execute(task);
+            for (TrackedNoOpRunnable task : tasks)
+                assertFalse(task.done);
+            try { p.shutdownNow(); } catch (SecurityException ok) { return; }
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * executor using DiscardOldestPolicy drops oldest task if saturated.
+     */
+    public void testSaturatedExecute4() {
+        RejectedExecutionHandler h = new ThreadPoolExecutor.DiscardOldestPolicy();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1),
+                                   h);
+        try {
+            p.execute(new TrackedLongRunnable());
+            TrackedLongRunnable r2 = new TrackedLongRunnable();
+            p.execute(r2);
+            assertTrue(p.getQueue().contains(r2));
+            TrackedNoOpRunnable r3 = new TrackedNoOpRunnable();
+            p.execute(r3);
+            assertFalse(p.getQueue().contains(r2));
+            assertTrue(p.getQueue().contains(r3));
+            try { p.shutdownNow(); } catch (SecurityException ok) { return; }
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * execute throws RejectedExecutionException if shutdown
+     */
+    public void testRejectedExecutionExceptionOnShutdown() {
+        ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1));
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try {
+            p.execute(new NoOpRunnable());
+            shouldThrow();
+        } catch (RejectedExecutionException success) {}
+
+        joinPool(p);
+    }
+
+    /**
+     * execute using CallerRunsPolicy drops task on shutdown
+     */
+    public void testCallerRunsOnShutdown() {
+        RejectedExecutionHandler h = new ThreadPoolExecutor.CallerRunsPolicy();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1), h);
+
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * execute using DiscardPolicy drops task on shutdown
+     */
+    public void testDiscardOnShutdown() {
+        RejectedExecutionHandler h = new ThreadPoolExecutor.DiscardPolicy();
+        ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1),
+                                   h);
+
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * execute using DiscardOldestPolicy drops task on shutdown
+     */
+    public void testDiscardOldestOnShutdown() {
+        RejectedExecutionHandler h = new ThreadPoolExecutor.DiscardOldestPolicy();
+        ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1),
+                                   h);
+
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * execute(null) throws NPE
+     */
+    public void testExecuteNull() {
+        ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            p.execute(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+
+        joinPool(p);
+    }
+
+    /**
+     * setCorePoolSize of negative value throws IllegalArgumentException
+     */
+    public void testCorePoolSizeIllegalArgumentException() {
+        ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            p.setCorePoolSize(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        joinPool(p);
+    }
+
+    /**
+     * setMaximumPoolSize(int) throws IllegalArgumentException if
+     * given a value less the core pool size
+     */
+    public void testMaximumPoolSizeIllegalArgumentException() {
+        ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 3,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            p.setMaximumPoolSize(1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        joinPool(p);
+    }
+
+    /**
+     * setMaximumPoolSize throws IllegalArgumentException
+     * if given a negative value
+     */
+    public void testMaximumPoolSizeIllegalArgumentException2() {
+        ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 3,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            p.setMaximumPoolSize(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        joinPool(p);
+    }
+
+    /**
+     * setKeepAliveTime throws IllegalArgumentException
+     * when given a negative value
+     */
+    public void testKeepAliveTimeIllegalArgumentException() {
+        ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 3,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            p.setKeepAliveTime(-1,MILLISECONDS);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        joinPool(p);
+    }
+
+    /**
+     * terminated() is called on termination
+     */
+    public void testTerminated() {
+        ExtendedTPE p = new ExtendedTPE();
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        assertTrue(p.terminatedCalled());
+        joinPool(p);
+    }
+
+    /**
+     * beforeExecute and afterExecute are called when executing task
+     */
+    public void testBeforeAfter() throws InterruptedException {
+        ExtendedTPE p = new ExtendedTPE();
+        try {
+            final CountDownLatch done = new CountDownLatch(1);
+            final CheckedRunnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                }};
+            p.execute(task);
+            await(p.afterCalled);
+            assertEquals(0, done.getCount());
+            assertTrue(p.afterCalled());
+            assertTrue(p.beforeCalled());
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * completed submit of callable returns result
+     */
+    public void testSubmitCallable() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            Future<String> future = e.submit(new StringTask());
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * completed submit of runnable returns successfully
+     */
+    public void testSubmitRunnable() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            Future<?> future = e.submit(new NoOpRunnable());
+            future.get();
+            assertTrue(future.isDone());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * completed submit of (runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NPE
+     */
+    public void testInvokeAny1() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            e.invokeAny(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IAE
+     */
+    public void testInvokeAny2() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            e.invokeAny(new ArrayList<Callable<String>>());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NPE if c has null elements
+     */
+    public void testInvokeAny3() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(latchAwaitingStringTask(latch));
+        l.add(null);
+        try {
+            e.invokeAny(l);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            latch.countDown();
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testInvokeAny4() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        try {
+            e.invokeAny(l);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task
+     */
+    public void testInvokeAny5() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NPE
+     */
+    public void testInvokeAll1() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            e.invokeAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws InterruptedException {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NPE if c has null elements
+     */
+    public void testInvokeAll3() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        l.add(null);
+        try {
+            e.invokeAll(l);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testInvokeAll4() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks
+     */
+    public void testInvokeAll5() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NPE
+     */
+    public void testTimedInvokeAny1() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(,,null) throws NPE
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IAE
+     */
+    public void testTimedInvokeAny2() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(latchAwaitingStringTask(latch));
+        l.add(null);
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            latch.countDown();
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        try {
+            e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task
+     */
+    public void testTimedInvokeAny5() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NPE
+     */
+    public void testTimedInvokeAll1() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(,,null) throws NPE
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        try {
+            e.invokeAll(l, MEDIUM_DELAY_MS, null);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws InterruptedException {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAll3() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new StringTask());
+        l.add(null);
+        try {
+            e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        List<Callable<String>> l = new ArrayList<Callable<String>>();
+        l.add(new NPETask());
+        List<Future<String>> futures =
+            e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+        assertEquals(1, futures.size());
+        try {
+            futures.get(0).get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertTrue(success.getCause() instanceof NullPointerException);
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks
+     */
+    public void testTimedInvokeAll5() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * timed invokeAll(c) cancels tasks not completed by timeout
+     */
+    public void testTimedInvokeAll6() throws Exception {
+        ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(Executors.callable(new MediumPossiblyInterruptedRunnable(), TEST_STRING));
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, SHORT_DELAY_MS, MILLISECONDS);
+            assertEquals(l.size(), futures.size());
+            for (Future future : futures)
+                assertTrue(future.isDone());
+            assertFalse(futures.get(0).isCancelled());
+            assertTrue(futures.get(1).isCancelled());
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * Execution continues if there is at least one thread even if
+     * thread factory fails to create more
+     */
+    public void testFailingThreadFactory() throws InterruptedException {
+        final ExecutorService e =
+            new ThreadPoolExecutor(100, 100,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new LinkedBlockingQueue<Runnable>(),
+                                   new FailingThreadFactory());
+        try {
+            final int TASKS = 100;
+            final CountDownLatch done = new CountDownLatch(TASKS);
+            for (int k = 0; k < TASKS; ++k)
+                e.execute(new CheckedRunnable() {
+                    public void realRun() {
+                        done.countDown();
+                    }});
+            assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
+        } finally {
+            joinPool(e);
+        }
+    }
+
+    /**
+     * allowsCoreThreadTimeOut is by default false.
+     */
+    public void testAllowsCoreThreadTimeOut() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 2,
+                                   1000, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        assertFalse(p.allowsCoreThreadTimeOut());
+        joinPool(p);
+    }
+
+    /**
+     * allowCoreThreadTimeOut(true) causes idle threads to time out
+     */
+    public void testAllowCoreThreadTimeOut_true() throws Exception {
+        long coreThreadTimeOut = SHORT_DELAY_MS;
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 10,
+                                   coreThreadTimeOut, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        try {
+            p.allowCoreThreadTimeOut(true);
+            p.execute(new CheckedRunnable() {
+                public void realRun() {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                }});
+            await(threadStarted);
+            delay(coreThreadTimeOut);
+            long startTime = System.nanoTime();
+            while (p.getPoolSize() > 0
+                   && millisElapsedSince(startTime) < LONG_DELAY_MS)
+                Thread.yield();
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            assertEquals(0, p.getPoolSize());
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * allowCoreThreadTimeOut(false) causes idle threads not to time out
+     */
+    public void testAllowCoreThreadTimeOut_false() throws Exception {
+        long coreThreadTimeOut = SHORT_DELAY_MS;
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 10,
+                                   coreThreadTimeOut, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        try {
+            p.allowCoreThreadTimeOut(false);
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertTrue(p.getPoolSize() >= 1);
+                }});
+            delay(2 * coreThreadTimeOut);
+            assertTrue(p.getPoolSize() >= 1);
+        } finally {
+            joinPool(p);
+        }
+    }
+
+    /**
+     * execute allows the same task to be submitted multiple times, even
+     * if rejected
+     */
+    public void testRejectedRecycledTask() throws InterruptedException {
+        final int nTasks = 1000;
+        final CountDownLatch done = new CountDownLatch(nTasks);
+        final Runnable recycledTask = new Runnable() {
+            public void run() {
+                done.countDown();
+            }};
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 30, 60, TimeUnit.SECONDS,
+                                   new ArrayBlockingQueue(30));
+        try {
+            for (int i = 0; i < nTasks; ++i) {
+                for (;;) {
+                    try {
+                        p.execute(recycledTask);
+                        break;
+                    }
+                    catch (RejectedExecutionException ignore) {}
+                }
+            }
+            // enough time to run all tasks
+            assertTrue(done.await(nTasks * SHORT_DELAY_MS, MILLISECONDS));
+        } finally {
+            joinPool(p);
+        }
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ThreadTest.java b/jsr166-tests/src/test/java/jsr166/ThreadTest.java
new file mode 100644
index 0000000..12c2f8a
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ThreadTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+
+public class ThreadTest extends JSR166TestCase {
+
+    static class MyHandler implements Thread.UncaughtExceptionHandler {
+        public void uncaughtException(Thread t, Throwable e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * getUncaughtExceptionHandler returns ThreadGroup unless set,
+     * otherwise returning value of last setUncaughtExceptionHandler.
+     */
+    public void testGetAndSetUncaughtExceptionHandler() {
+        // these must be done all at once to avoid state
+        // dependencies across tests
+        Thread current = Thread.currentThread();
+        ThreadGroup tg = current.getThreadGroup();
+        MyHandler eh = new MyHandler();
+        assertEquals(tg, current.getUncaughtExceptionHandler());
+        current.setUncaughtExceptionHandler(eh);
+        assertEquals(eh, current.getUncaughtExceptionHandler());
+        current.setUncaughtExceptionHandler(null);
+        assertEquals(tg, current.getUncaughtExceptionHandler());
+    }
+
+    /**
+     * getDefaultUncaughtExceptionHandler returns value of last
+     * setDefaultUncaughtExceptionHandler.
+     */
+    public void testGetAndSetDefaultUncaughtExceptionHandler() {
+        // BEGIN android-remove (when running as cts the RuntimeInit will 
+        // set a default handler)
+        // assertEquals(null, Thread.getDefaultUncaughtExceptionHandler());
+        // END android-remove
+
+        // failure due to securityException is OK.
+        // Would be nice to explicitly test both ways, but cannot yet.
+        try {
+            Thread current = Thread.currentThread();
+            ThreadGroup tg = current.getThreadGroup();
+            MyHandler eh = new MyHandler();
+            Thread.setDefaultUncaughtExceptionHandler(eh);
+            assertEquals(eh, Thread.getDefaultUncaughtExceptionHandler());
+            Thread.setDefaultUncaughtExceptionHandler(null);
+        }
+        catch (SecurityException ok) {
+        }
+        assertEquals(null, Thread.getDefaultUncaughtExceptionHandler());
+    }
+
+    // How to test actually using UEH within junit?
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/TimeUnitTest.java b/jsr166-tests/src/test/java/jsr166/TimeUnitTest.java
new file mode 100644
index 0000000..7fa9e1a
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/TimeUnitTest.java
@@ -0,0 +1,457 @@
+/*
+ * 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.
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class TimeUnitTest extends JSR166TestCase {
+
+    // (loops to 88888 check increments at all time divisions.)
+
+    /**
+     * convert correctly converts sample values across the units
+     */
+    public void testConvert() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*60*60*24,
+                         TimeUnit.SECONDS.convert(t,
+                                                  TimeUnit.DAYS));
+            assertEquals(t*60*60,
+                         TimeUnit.SECONDS.convert(t,
+                                                  TimeUnit.HOURS));
+            assertEquals(t*60,
+                         TimeUnit.SECONDS.convert(t,
+                                                  TimeUnit.MINUTES));
+            assertEquals(t,
+                         TimeUnit.SECONDS.convert(t,
+                                                  TimeUnit.SECONDS));
+            assertEquals(t,
+                         TimeUnit.SECONDS.convert(1000L*t,
+                                                  TimeUnit.MILLISECONDS));
+            assertEquals(t,
+                         TimeUnit.SECONDS.convert(1000000L*t,
+                                                  TimeUnit.MICROSECONDS));
+            assertEquals(t,
+                         TimeUnit.SECONDS.convert(1000000000L*t,
+                                                  TimeUnit.NANOSECONDS));
+
+            assertEquals(1000L*t*60*60*24,
+                         TimeUnit.MILLISECONDS.convert(t,
+                                                  TimeUnit.DAYS));
+            assertEquals(1000L*t*60*60,
+                         TimeUnit.MILLISECONDS.convert(t,
+                                                  TimeUnit.HOURS));
+            assertEquals(1000L*t*60,
+                         TimeUnit.MILLISECONDS.convert(t,
+                                                  TimeUnit.MINUTES));
+            assertEquals(1000L*t,
+                         TimeUnit.MILLISECONDS.convert(t,
+                                                  TimeUnit.SECONDS));
+            assertEquals(t,
+                         TimeUnit.MILLISECONDS.convert(t,
+                                                  TimeUnit.MILLISECONDS));
+            assertEquals(t,
+                         TimeUnit.MILLISECONDS.convert(1000L*t,
+                                                  TimeUnit.MICROSECONDS));
+            assertEquals(t,
+                         TimeUnit.MILLISECONDS.convert(1000000L*t,
+                                                  TimeUnit.NANOSECONDS));
+
+            assertEquals(1000000L*t*60*60*24,
+                         TimeUnit.MICROSECONDS.convert(t,
+                                                  TimeUnit.DAYS));
+            assertEquals(1000000L*t*60*60,
+                         TimeUnit.MICROSECONDS.convert(t,
+                                                  TimeUnit.HOURS));
+            assertEquals(1000000L*t*60,
+                         TimeUnit.MICROSECONDS.convert(t,
+                                                  TimeUnit.MINUTES));
+            assertEquals(1000000L*t,
+                         TimeUnit.MICROSECONDS.convert(t,
+                                                  TimeUnit.SECONDS));
+            assertEquals(1000L*t,
+                         TimeUnit.MICROSECONDS.convert(t,
+                                                  TimeUnit.MILLISECONDS));
+            assertEquals(t,
+                         TimeUnit.MICROSECONDS.convert(t,
+                                                  TimeUnit.MICROSECONDS));
+            assertEquals(t,
+                         TimeUnit.MICROSECONDS.convert(1000L*t,
+                                                  TimeUnit.NANOSECONDS));
+
+            assertEquals(1000000000L*t*60*60*24,
+                         TimeUnit.NANOSECONDS.convert(t,
+                                                  TimeUnit.DAYS));
+            assertEquals(1000000000L*t*60*60,
+                         TimeUnit.NANOSECONDS.convert(t,
+                                                  TimeUnit.HOURS));
+            assertEquals(1000000000L*t*60,
+                         TimeUnit.NANOSECONDS.convert(t,
+                                                  TimeUnit.MINUTES));
+            assertEquals(1000000000L*t,
+                         TimeUnit.NANOSECONDS.convert(t,
+                                                  TimeUnit.SECONDS));
+            assertEquals(1000000L*t,
+                         TimeUnit.NANOSECONDS.convert(t,
+                                                  TimeUnit.MILLISECONDS));
+            assertEquals(1000L*t,
+                         TimeUnit.NANOSECONDS.convert(t,
+                                                  TimeUnit.MICROSECONDS));
+            assertEquals(t,
+                         TimeUnit.NANOSECONDS.convert(t,
+                                                  TimeUnit.NANOSECONDS));
+        }
+    }
+
+    /**
+     * toNanos correctly converts sample values in different units to
+     * nanoseconds
+     */
+    public void testToNanos() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*1000000000L*60*60*24,
+                         TimeUnit.DAYS.toNanos(t));
+            assertEquals(t*1000000000L*60*60,
+                         TimeUnit.HOURS.toNanos(t));
+            assertEquals(t*1000000000L*60,
+                         TimeUnit.MINUTES.toNanos(t));
+            assertEquals(1000000000L*t,
+                         TimeUnit.SECONDS.toNanos(t));
+            assertEquals(1000000L*t,
+                         TimeUnit.MILLISECONDS.toNanos(t));
+            assertEquals(1000L*t,
+                         TimeUnit.MICROSECONDS.toNanos(t));
+            assertEquals(t,
+                         TimeUnit.NANOSECONDS.toNanos(t));
+        }
+    }
+
+    /**
+     * toMicros correctly converts sample values in different units to
+     * microseconds
+     */
+    public void testToMicros() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*1000000L*60*60*24,
+                         TimeUnit.DAYS.toMicros(t));
+            assertEquals(t*1000000L*60*60,
+                         TimeUnit.HOURS.toMicros(t));
+            assertEquals(t*1000000L*60,
+                         TimeUnit.MINUTES.toMicros(t));
+            assertEquals(1000000L*t,
+                         TimeUnit.SECONDS.toMicros(t));
+            assertEquals(1000L*t,
+                         TimeUnit.MILLISECONDS.toMicros(t));
+            assertEquals(t,
+                         TimeUnit.MICROSECONDS.toMicros(t));
+            assertEquals(t,
+                         TimeUnit.NANOSECONDS.toMicros(t*1000L));
+        }
+    }
+
+    /**
+     * toMillis correctly converts sample values in different units to
+     * milliseconds
+     */
+    public void testToMillis() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*1000L*60*60*24,
+                         TimeUnit.DAYS.toMillis(t));
+            assertEquals(t*1000L*60*60,
+                         TimeUnit.HOURS.toMillis(t));
+            assertEquals(t*1000L*60,
+                         TimeUnit.MINUTES.toMillis(t));
+            assertEquals(1000L*t,
+                         TimeUnit.SECONDS.toMillis(t));
+            assertEquals(t,
+                         TimeUnit.MILLISECONDS.toMillis(t));
+            assertEquals(t,
+                         TimeUnit.MICROSECONDS.toMillis(t*1000L));
+            assertEquals(t,
+                         TimeUnit.NANOSECONDS.toMillis(t*1000000L));
+        }
+    }
+
+    /**
+     * toSeconds correctly converts sample values in different units to
+     * seconds
+     */
+    public void testToSeconds() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*60*60*24,
+                         TimeUnit.DAYS.toSeconds(t));
+            assertEquals(t*60*60,
+                         TimeUnit.HOURS.toSeconds(t));
+            assertEquals(t*60,
+                         TimeUnit.MINUTES.toSeconds(t));
+            assertEquals(t,
+                         TimeUnit.SECONDS.toSeconds(t));
+            assertEquals(t,
+                         TimeUnit.MILLISECONDS.toSeconds(t*1000L));
+            assertEquals(t,
+                         TimeUnit.MICROSECONDS.toSeconds(t*1000000L));
+            assertEquals(t,
+                         TimeUnit.NANOSECONDS.toSeconds(t*1000000000L));
+        }
+    }
+
+    /**
+     * toMinutes correctly converts sample values in different units to
+     * minutes
+     */
+    public void testToMinutes() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*60*24,
+                         TimeUnit.DAYS.toMinutes(t));
+            assertEquals(t*60,
+                         TimeUnit.HOURS.toMinutes(t));
+            assertEquals(t,
+                         TimeUnit.MINUTES.toMinutes(t));
+            assertEquals(t,
+                         TimeUnit.SECONDS.toMinutes(t*60));
+            assertEquals(t,
+                         TimeUnit.MILLISECONDS.toMinutes(t*1000L*60));
+            assertEquals(t,
+                         TimeUnit.MICROSECONDS.toMinutes(t*1000000L*60));
+            assertEquals(t,
+                         TimeUnit.NANOSECONDS.toMinutes(t*1000000000L*60));
+        }
+    }
+
+    /**
+     * toHours correctly converts sample values in different units to
+     * hours
+     */
+    public void testToHours() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*24,
+                         TimeUnit.DAYS.toHours(t));
+            assertEquals(t,
+                         TimeUnit.HOURS.toHours(t));
+            assertEquals(t,
+                         TimeUnit.MINUTES.toHours(t*60));
+            assertEquals(t,
+                         TimeUnit.SECONDS.toHours(t*60*60));
+            assertEquals(t,
+                         TimeUnit.MILLISECONDS.toHours(t*1000L*60*60));
+            assertEquals(t,
+                         TimeUnit.MICROSECONDS.toHours(t*1000000L*60*60));
+            assertEquals(t,
+                         TimeUnit.NANOSECONDS.toHours(t*1000000000L*60*60));
+        }
+    }
+
+    /**
+     * toDays correctly converts sample values in different units to
+     * days
+     */
+    public void testToDays() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t,
+                         TimeUnit.DAYS.toDays(t));
+            assertEquals(t,
+                         TimeUnit.HOURS.toDays(t*24));
+            assertEquals(t,
+                         TimeUnit.MINUTES.toDays(t*60*24));
+            assertEquals(t,
+                         TimeUnit.SECONDS.toDays(t*60*60*24));
+            assertEquals(t,
+                         TimeUnit.MILLISECONDS.toDays(t*1000L*60*60*24));
+            assertEquals(t,
+                         TimeUnit.MICROSECONDS.toDays(t*1000000L*60*60*24));
+            assertEquals(t,
+                         TimeUnit.NANOSECONDS.toDays(t*1000000000L*60*60*24));
+        }
+    }
+
+    /**
+     * convert saturates positive too-large values to Long.MAX_VALUE
+     * and negative to LONG.MIN_VALUE
+     */
+    public void testConvertSaturate() {
+        assertEquals(Long.MAX_VALUE,
+                     TimeUnit.NANOSECONDS.convert(Long.MAX_VALUE / 2,
+                                                  TimeUnit.SECONDS));
+        assertEquals(Long.MIN_VALUE,
+                     TimeUnit.NANOSECONDS.convert(-Long.MAX_VALUE / 4,
+                                                  TimeUnit.SECONDS));
+        assertEquals(Long.MAX_VALUE,
+                     TimeUnit.NANOSECONDS.convert(Long.MAX_VALUE / 2,
+                                                  TimeUnit.MINUTES));
+        assertEquals(Long.MIN_VALUE,
+                     TimeUnit.NANOSECONDS.convert(-Long.MAX_VALUE / 4,
+                                                  TimeUnit.MINUTES));
+        assertEquals(Long.MAX_VALUE,
+                     TimeUnit.NANOSECONDS.convert(Long.MAX_VALUE / 2,
+                                                  TimeUnit.HOURS));
+        assertEquals(Long.MIN_VALUE,
+                     TimeUnit.NANOSECONDS.convert(-Long.MAX_VALUE / 4,
+                                                  TimeUnit.HOURS));
+        assertEquals(Long.MAX_VALUE,
+                     TimeUnit.NANOSECONDS.convert(Long.MAX_VALUE / 2,
+                                                  TimeUnit.DAYS));
+        assertEquals(Long.MIN_VALUE,
+                     TimeUnit.NANOSECONDS.convert(-Long.MAX_VALUE / 4,
+                                                  TimeUnit.DAYS));
+    }
+
+    /**
+     * toNanos saturates positive too-large values to Long.MAX_VALUE
+     * and negative to LONG.MIN_VALUE
+     */
+    public void testToNanosSaturate() {
+        assertEquals(Long.MAX_VALUE,
+                     TimeUnit.MILLISECONDS.toNanos(Long.MAX_VALUE / 2));
+        assertEquals(Long.MIN_VALUE,
+                     TimeUnit.MILLISECONDS.toNanos(-Long.MAX_VALUE / 3));
+    }
+
+    /**
+     * toString returns name of unit
+     */
+    public void testToString() {
+        assertEquals("SECONDS", TimeUnit.SECONDS.toString());
+    }
+
+    /**
+     * name returns name of unit
+     */
+    public void testName() {
+        assertEquals("SECONDS", TimeUnit.SECONDS.name());
+    }
+
+    /**
+     * Timed wait without holding lock throws
+     * IllegalMonitorStateException
+     */
+    public void testTimedWait_IllegalMonitorException() {
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Object o = new Object();
+                TimeUnit tu = TimeUnit.MILLISECONDS;
+
+                try {
+                    tu.timedWait(o, LONG_DELAY_MS);
+                    threadShouldThrow();
+                } catch (IllegalMonitorStateException success) {}
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * timedWait throws InterruptedException when interrupted
+     */
+    public void testTimedWait_Interruptible() {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Object o = new Object();
+                TimeUnit tu = TimeUnit.MILLISECONDS;
+
+                Thread.currentThread().interrupt();
+                try {
+                    synchronized (o) {
+                        tu.timedWait(o, LONG_DELAY_MS);
+                    }
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    synchronized (o) {
+                        tu.timedWait(o, LONG_DELAY_MS);
+                    }
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timedJoin throws InterruptedException when interrupted
+     */
+    public void testTimedJoin_Interruptible() {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        final Thread s = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                Thread.sleep(LONG_DELAY_MS);
+            }});
+        final Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                TimeUnit tu = TimeUnit.MILLISECONDS;
+                Thread.currentThread().interrupt();
+                try {
+                    tu.timedJoin(s, LONG_DELAY_MS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    tu.timedJoin(s, LONG_DELAY_MS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        s.interrupt();
+        awaitTermination(s);
+    }
+
+    /**
+     * timedSleep throws InterruptedException when interrupted
+     */
+    public void testTimedSleep_Interruptible() {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                TimeUnit tu = TimeUnit.MILLISECONDS;
+                Thread.currentThread().interrupt();
+                try {
+                    tu.sleep(LONG_DELAY_MS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    tu.sleep(LONG_DELAY_MS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * a deserialized serialized unit is the same instance
+     */
+    public void testSerialization() throws Exception {
+        TimeUnit x = TimeUnit.MILLISECONDS;
+        assertSame(x, serialClone(x));
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/TreeMapTest.java b/jsr166-tests/src/test/java/jsr166/TreeMapTest.java
new file mode 100644
index 0000000..87baa1a
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/TreeMapTest.java
@@ -0,0 +1,1068 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+
+public class TreeMapTest extends JSR166TestCase {
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static TreeMap map5() {
+        TreeMap map = new TreeMap();
+        assertTrue(map.isEmpty());
+        map.put(one, "A");
+        map.put(five, "E");
+        map.put(three, "C");
+        map.put(two, "B");
+        map.put(four, "D");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map;
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testClear() {
+        TreeMap map = map5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * copy constructor creates map equal to source map
+     */
+    public void testConstructFromSorted() {
+        TreeMap map = map5();
+        TreeMap map2 = new TreeMap(map);
+        assertEquals(map, map2);
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testEquals() {
+        TreeMap map1 = map5();
+        TreeMap map2 = map5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testContainsKey() {
+        TreeMap map = map5();
+        assertTrue(map.containsKey(one));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testContainsValue() {
+        TreeMap map = map5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testGet() {
+        TreeMap map = map5();
+        assertEquals("A", (String)map.get(one));
+        TreeMap empty = new TreeMap();
+        assertNull(empty.get(one));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testIsEmpty() {
+        TreeMap empty = new TreeMap();
+        TreeMap map = map5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testFirstKey() {
+        TreeMap map = map5();
+        assertEquals(one, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testLastKey() {
+        TreeMap map = map5();
+        assertEquals(five, map.lastKey());
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testKeySetToArray() {
+        TreeMap map = map5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * descendingkeySet.toArray returns contains all keys
+     */
+    public void testDescendingKeySetToArray() {
+        TreeMap map = map5();
+        Set s = map.descendingKeySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testKeySet() {
+        TreeMap map = map5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(one));
+        assertTrue(s.contains(two));
+        assertTrue(s.contains(three));
+        assertTrue(s.contains(four));
+        assertTrue(s.contains(five));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testKeySetOrder() {
+        TreeMap map = map5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descending iterator of key set is inverse ordered
+     */
+    public void testKeySetDescendingIteratorOrder() {
+        TreeMap map = map5();
+        NavigableSet s = map.navigableKeySet();
+        Iterator i = s.descendingIterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, five);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descendingKeySet is ordered
+     */
+    public void testDescendingKeySetOrder() {
+        TreeMap map = map5();
+        Set s = map.descendingKeySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, five);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descending iterator of descendingKeySet is ordered
+     */
+    public void testDescendingKeySetDescendingIteratorOrder() {
+        TreeMap map = map5();
+        NavigableSet s = map.descendingKeySet();
+        Iterator i = s.descendingIterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testValues() {
+        TreeMap map = map5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testEntrySet() {
+        TreeMap map = map5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * descendingEntrySet contains all pairs
+     */
+    public void testDescendingEntrySet() {
+        TreeMap map = map5();
+        Set s = map.descendingMap().entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * entrySet.toArray contains all entries
+     */
+    public void testEntrySetToArray() {
+        TreeMap map = map5();
+        Set s = map.entrySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        for (int i = 0; i < 5; ++i) {
+            assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
+            assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
+        }
+    }
+
+    /**
+     * descendingEntrySet.toArray contains all entries
+     */
+    public void testDescendingEntrySetToArray() {
+        TreeMap map = map5();
+        Set s = map.descendingMap().entrySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        for (int i = 0; i < 5; ++i) {
+            assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
+            assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testPutAll() {
+        TreeMap empty = new TreeMap();
+        TreeMap map = map5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(one));
+        assertTrue(empty.containsKey(two));
+        assertTrue(empty.containsKey(three));
+        assertTrue(empty.containsKey(four));
+        assertTrue(empty.containsKey(five));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testRemove() {
+        TreeMap map = map5();
+        map.remove(five);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testLowerEntry() {
+        TreeMap map = map5();
+        Map.Entry e1 = map.lowerEntry(three);
+        assertEquals(two, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(one);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testHigherEntry() {
+        TreeMap map = map5();
+        Map.Entry e1 = map.higherEntry(three);
+        assertEquals(four, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(five);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testFloorEntry() {
+        TreeMap map = map5();
+        Map.Entry e1 = map.floorEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(one);
+        assertEquals(one, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testCeilingEntry() {
+        TreeMap map = map5();
+        Map.Entry e1 = map.ceilingEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(five);
+        assertEquals(five, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * lowerKey returns preceding element
+     */
+    public void testLowerKey() {
+        TreeMap q = map5();
+        Object e1 = q.lowerKey(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lowerKey(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lowerKey(one);
+        assertNull(e3);
+
+        Object e4 = q.lowerKey(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherKey returns next element
+     */
+    public void testHigherKey() {
+        TreeMap q = map5();
+        Object e1 = q.higherKey(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higherKey(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higherKey(five);
+        assertNull(e3);
+
+        Object e4 = q.higherKey(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorKey returns preceding element
+     */
+    public void testFloorKey() {
+        TreeMap q = map5();
+        Object e1 = q.floorKey(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floorKey(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floorKey(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floorKey(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingKey returns next element
+     */
+    public void testCeilingKey() {
+        TreeMap q = map5();
+        Object e1 = q.ceilingKey(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceilingKey(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceilingKey(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceilingKey(six);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testPollFirstEntry() {
+        TreeMap map = map5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(two, e.getKey());
+        map.put(one, "A");
+        e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(three, e.getKey());
+        map.remove(four);
+        e = map.pollFirstEntry();
+        assertEquals(five, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testPollLastEntry() {
+        TreeMap map = map5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(four, e.getKey());
+        map.put(five, "E");
+        e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(three, e.getKey());
+        map.remove(two);
+        e = map.pollLastEntry();
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testSize() {
+        TreeMap map = map5();
+        TreeMap empty = new TreeMap();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        TreeMap map = map5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception tests
+
+    /**
+     * get(null) of nonempty map throws NPE
+     */
+    public void testGet_NullPointerException() {
+        try {
+            TreeMap c = map5();
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) of nonempty map throws NPE
+     */
+    public void testContainsKey_NullPointerException() {
+        try {
+            TreeMap c = map5();
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE for nonempty map
+     */
+    public void testRemove1_NullPointerException() {
+        try {
+            TreeMap c = new TreeMap();
+            c.put("sadsdf", "asdads");
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testSerialization() throws Exception {
+        NavigableMap x = map5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testSubMapContents() {
+        TreeMap map = map5();
+        NavigableMap sm = map.subMap(two, true, four, false);
+        assertEquals(two, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(three, k);
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals("C", sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testSubMapContents2() {
+        TreeMap map = map5();
+        NavigableMap sm = map.subMap(two, true, three, false);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.firstKey());
+        assertEquals(two, sm.lastKey());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertFalse(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(three), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testHeadMapContents() {
+        TreeMap map = map5();
+        NavigableMap sm = map.headMap(four, false);
+        assertTrue(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(four, map.firstKey());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testTailMapContents() {
+        TreeMap map = map5();
+        NavigableMap sm = map.tailMap(two, true);
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertTrue(sm.containsKey(four));
+        assertTrue(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(five, k);
+        k = (Integer)(r.next());
+        assertEquals(four, k);
+        k = (Integer)(r.next());
+        assertEquals(three, k);
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(two, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(three, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(four, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        NavigableMap ssm = sm.tailMap(four, true);
+        assertEquals(four, ssm.firstKey());
+        assertEquals(five, ssm.lastKey());
+        assertEquals("D", ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+    Random rnd = new Random(666);
+    BitSet bs;
+
+    /**
+     * Submaps of submaps subdivide correctly
+     */
+    public void testRecursiveSubMaps() throws Exception {
+        int mapSize = expensiveTests ? 1000 : 100;
+        Class cl = TreeMap.class;
+        NavigableMap<Integer, Integer> map = newMap(cl);
+        bs = new BitSet(mapSize);
+
+        populate(map, mapSize);
+        check(map,                 0, mapSize - 1, true);
+        check(map.descendingMap(), 0, mapSize - 1, false);
+
+        mutateMap(map, 0, mapSize - 1);
+        check(map,                 0, mapSize - 1, true);
+        check(map.descendingMap(), 0, mapSize - 1, false);
+
+        bashSubMap(map.subMap(0, true, mapSize, false),
+                   0, mapSize - 1, true);
+    }
+
+    static NavigableMap<Integer, Integer> newMap(Class cl) throws Exception {
+        NavigableMap<Integer, Integer> result
+            = (NavigableMap<Integer, Integer>) cl.newInstance();
+        assertEquals(0, result.size());
+        assertFalse(result.keySet().iterator().hasNext());
+        return result;
+    }
+
+    void populate(NavigableMap<Integer, Integer> map, int limit) {
+        for (int i = 0, n = 2 * limit / 3; i < n; i++) {
+            int key = rnd.nextInt(limit);
+            put(map, key);
+        }
+    }
+
+    void mutateMap(NavigableMap<Integer, Integer> map, int min, int max) {
+        int size = map.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(map, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = map.keySet().iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (map.size() < size) {
+            int key = min + rnd.nextInt(rangeSize);
+            assertTrue(key >= min && key<= max);
+            put(map, key);
+        }
+    }
+
+    void mutateSubMap(NavigableMap<Integer, Integer> map, int min, int max) {
+        int size = map.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(map, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = map.keySet().iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (map.size() < size) {
+            int key = min - 5 + rnd.nextInt(rangeSize + 10);
+            if (key >= min && key<= max) {
+                put(map, key);
+            } else {
+                try {
+                    map.put(key, 2 * key);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+            }
+        }
+    }
+
+    void put(NavigableMap<Integer, Integer> map, int key) {
+        if (map.put(key, 2 * key) == null)
+            bs.set(key);
+    }
+
+    void remove(NavigableMap<Integer, Integer> map, int key) {
+        if (map.remove(key) != null)
+            bs.clear(key);
+    }
+
+    void bashSubMap(NavigableMap<Integer, Integer> map,
+                    int min, int max, boolean ascending) {
+        check(map, min, max, ascending);
+        check(map.descendingMap(), min, max, !ascending);
+
+        mutateSubMap(map, min, max);
+        check(map, min, max, ascending);
+        check(map.descendingMap(), min, max, !ascending);
+
+        // Recurse
+        if (max - min < 2)
+            return;
+        int midPoint = (min + max) / 2;
+
+        // headMap - pick direction and endpoint inclusion randomly
+        boolean incl = rnd.nextBoolean();
+        NavigableMap<Integer,Integer> hm = map.headMap(midPoint, incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubMap(hm, min, midPoint - (incl ? 0 : 1), true);
+            else
+                bashSubMap(hm.descendingMap(), min, midPoint - (incl ? 0 : 1),
+                           false);
+        } else {
+            if (rnd.nextBoolean())
+                bashSubMap(hm, midPoint + (incl ? 0 : 1), max, false);
+            else
+                bashSubMap(hm.descendingMap(), midPoint + (incl ? 0 : 1), max,
+                           true);
+        }
+
+        // tailMap - pick direction and endpoint inclusion randomly
+        incl = rnd.nextBoolean();
+        NavigableMap<Integer,Integer> tm = map.tailMap(midPoint,incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubMap(tm, midPoint + (incl ? 0 : 1), max, true);
+            else
+                bashSubMap(tm.descendingMap(), midPoint + (incl ? 0 : 1), max,
+                           false);
+        } else {
+            if (rnd.nextBoolean()) {
+                bashSubMap(tm, min, midPoint - (incl ? 0 : 1), false);
+            } else {
+                bashSubMap(tm.descendingMap(), min, midPoint - (incl ? 0 : 1),
+                           true);
+            }
+        }
+
+        // subMap - pick direction and endpoint inclusion randomly
+        int rangeSize = max - min + 1;
+        int[] endpoints = new int[2];
+        endpoints[0] = min + rnd.nextInt(rangeSize);
+        endpoints[1] = min + rnd.nextInt(rangeSize);
+        Arrays.sort(endpoints);
+        boolean lowIncl = rnd.nextBoolean();
+        boolean highIncl = rnd.nextBoolean();
+        if (ascending) {
+            NavigableMap<Integer,Integer> sm = map.subMap(
+                endpoints[0], lowIncl, endpoints[1], highIncl);
+            if (rnd.nextBoolean())
+                bashSubMap(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+            else
+                bashSubMap(sm.descendingMap(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+        } else {
+            NavigableMap<Integer,Integer> sm = map.subMap(
+                endpoints[1], highIncl, endpoints[0], lowIncl);
+            if (rnd.nextBoolean())
+                bashSubMap(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+            else
+                bashSubMap(sm.descendingMap(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+        }
+    }
+
+    /**
+     * min and max are both inclusive.  If max < min, interval is empty.
+     */
+    void check(NavigableMap<Integer, Integer> map,
+                      final int min, final int max, final boolean ascending) {
+        class ReferenceSet {
+            int lower(int key) {
+                return ascending ? lowerAscending(key) : higherAscending(key);
+            }
+            int floor(int key) {
+                return ascending ? floorAscending(key) : ceilingAscending(key);
+            }
+            int ceiling(int key) {
+                return ascending ? ceilingAscending(key) : floorAscending(key);
+            }
+            int higher(int key) {
+                return ascending ? higherAscending(key) : lowerAscending(key);
+            }
+            int first() {
+                return ascending ? firstAscending() : lastAscending();
+            }
+            int last() {
+                return ascending ? lastAscending() : firstAscending();
+            }
+            int lowerAscending(int key) {
+                return floorAscending(key - 1);
+            }
+            int floorAscending(int key) {
+                if (key < min)
+                    return -1;
+                else if (key > max)
+                    key = max;
+
+                // BitSet should support this! Test would run much faster
+                while (key >= min) {
+                    if (bs.get(key))
+                        return key;
+                    key--;
+                }
+                return -1;
+            }
+            int ceilingAscending(int key) {
+                if (key < min)
+                    key = min;
+                else if (key > max)
+                    return -1;
+                int result = bs.nextSetBit(key);
+                return result > max ? -1 : result;
+            }
+            int higherAscending(int key) {
+                return ceilingAscending(key + 1);
+            }
+            private int firstAscending() {
+                int result = ceilingAscending(min);
+                return result > max ? -1 : result;
+            }
+            private int lastAscending() {
+                int result = floorAscending(max);
+                return result < min ? -1 : result;
+            }
+        }
+        ReferenceSet rs = new ReferenceSet();
+
+        // Test contents using containsKey
+        int size = 0;
+        for (int i = min; i <= max; i++) {
+            boolean bsContainsI = bs.get(i);
+            assertEquals(bsContainsI, map.containsKey(i));
+            if (bsContainsI)
+                size++;
+        }
+        assertEquals(size, map.size());
+
+        // Test contents using contains keySet iterator
+        int size2 = 0;
+        int previousKey = -1;
+        for (int key : map.keySet()) {
+            assertTrue(bs.get(key));
+            size2++;
+            assertTrue(previousKey < 0 ||
+                (ascending ? key - previousKey > 0 : key - previousKey < 0));
+            previousKey = key;
+        }
+        assertEquals(size2, size);
+
+        // Test navigation ops
+        for (int key = min - 1; key <= max + 1; key++) {
+            assertEq(map.lowerKey(key), rs.lower(key));
+            assertEq(map.floorKey(key), rs.floor(key));
+            assertEq(map.higherKey(key), rs.higher(key));
+            assertEq(map.ceilingKey(key), rs.ceiling(key));
+        }
+
+        // Test extrema
+        if (map.size() != 0) {
+            assertEq(map.firstKey(), rs.first());
+            assertEq(map.lastKey(), rs.last());
+        } else {
+            assertEq(rs.first(), -1);
+            assertEq(rs.last(),  -1);
+            try {
+                map.firstKey();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                map.lastKey();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+        }
+    }
+
+    static void assertEq(Integer i, int j) {
+        if (i == null)
+            assertEquals(j, -1);
+        else
+            assertEquals((int) i, j);
+    }
+
+    static boolean eq(Integer i, int j) {
+        return i == null ? j == -1 : i == j;
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/TreeSetTest.java b/jsr166-tests/src/test/java/jsr166/TreeSetTest.java
new file mode 100644
index 0000000..2957019
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/TreeSetTest.java
@@ -0,0 +1,985 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NavigableSet;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+public class TreeSetTest extends JSR166TestCase {
+
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * The number of elements to place in collections, arrays, etc.
+     */
+    static final int SIZE = 20;
+
+    /**
+     * Returns a new set of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private TreeSet<Integer> populatedSet(int n) {
+        TreeSet<Integer> q = new TreeSet<Integer>();
+        assertTrue(q.isEmpty());
+        for (int i = n-1; i >= 0; i-=2)
+            assertTrue(q.add(new Integer(i)));
+        for (int i = (n & 1); i < n; i+=2)
+            assertTrue(q.add(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * Returns a new set of first 5 ints.
+     */
+    private TreeSet set5() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        q.add(four);
+        q.add(five);
+        assertEquals(5, q.size());
+        return q;
+    }
+
+    /**
+     * A new set has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(0, new TreeSet().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            TreeSet q = new TreeSet((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            Integer[] ints = new Integer[SIZE];
+            TreeSet q = new TreeSet(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        try {
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i);
+            TreeSet q = new TreeSet(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        TreeSet q = new TreeSet(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * The comparator used in constructor is used
+     */
+    public void testConstructor7() {
+        MyReverseComparator cmp = new MyReverseComparator();
+        TreeSet q = new TreeSet(cmp);
+        assertEquals(cmp, q.comparator());
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        q.addAll(Arrays.asList(ints));
+        for (int i = SIZE-1; i >= 0; --i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.pollFirst();
+        q.pollFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        TreeSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * add(null) throws NPE if nonempty
+     */
+    public void testAddNull() {
+        try {
+            TreeSet q = populatedSet(SIZE);
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testAdd() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.add(zero));
+        assertTrue(q.add(one));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testAddDup() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.add(zero));
+        assertFalse(q.add(zero));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testAddNonComparable() {
+        try {
+            TreeSet q = new TreeSet();
+            q.add(new Object());
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        try {
+            TreeSet q = new TreeSet();
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        try {
+            TreeSet q = new TreeSet();
+            Integer[] ints = new Integer[SIZE];
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        try {
+            TreeSet q = new TreeSet();
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i);
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE-1-i);
+        TreeSet q = new TreeSet();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.pollFirst());
+    }
+
+    /**
+     * pollFirst succeeds unless empty
+     */
+    public void testPollFirst() {
+        TreeSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * pollLast succeeds unless empty
+     */
+    public void testPollLast() {
+        TreeSet q = populatedSet(SIZE);
+        for (int i = SIZE-1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        TreeSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i-1));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i+1));
+            assertFalse(q.contains(i+1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        TreeSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        TreeSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        TreeSet q = populatedSet(SIZE);
+        TreeSet p = new TreeSet();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        TreeSet q = populatedSet(SIZE);
+        TreeSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE-i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            TreeSet q = populatedSet(SIZE);
+            TreeSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE-i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer I = (Integer)(p.pollFirst());
+                assertFalse(q.contains(I));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testLower() {
+        TreeSet q = set5();
+        Object e1 = q.lower(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lower(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lower(one);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testHigher() {
+        TreeSet q = set5();
+        Object e1 = q.higher(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higher(five);
+        assertNull(e3);
+
+        Object e4 = q.higher(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testFloor() {
+        TreeSet q = set5();
+        Object e1 = q.floor(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floor(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floor(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testCeiling() {
+        TreeSet q = set5();
+        Object e1 = q.ceiling(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceiling(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceiling(six);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements in sorted order
+     */
+    public void testToArray() {
+        TreeSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements in sorted order
+     */
+    public void testToArray2() {
+        TreeSet<Integer> q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        TreeSet q = populatedSet(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testEmptyIterator() {
+        TreeSet q = new TreeSet();
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(0, i);
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final TreeSet q = new TreeSet();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        TreeSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testSerialization() throws Exception {
+        NavigableSet x = populatedSet(SIZE);
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testSubSetContents() {
+        TreeSet set = set5();
+        SortedSet sm = set.subSet(two, four);
+        assertEquals(two, sm.first());
+        assertEquals(three, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.first());
+        assertEquals(three, sm.last());
+        assertTrue(sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testSubSetContents2() {
+        TreeSet set = set5();
+        SortedSet sm = set.subSet(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.first());
+        assertEquals(two, sm.last());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertFalse(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(three));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testHeadSetContents() {
+        TreeSet set = set5();
+        SortedSet sm = set.headSet(four);
+        assertTrue(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(four, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testTailSetContents() {
+        TreeSet set = set5();
+        SortedSet sm = set.tailSet(two);
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertTrue(sm.contains(four));
+        assertTrue(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(four);
+        assertEquals(four, ssm.first());
+        assertEquals(five, ssm.last());
+        assertTrue(ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+    Random rnd = new Random(666);
+    BitSet bs;
+
+    /**
+     * Subsets of subsets subdivide correctly
+     */
+    public void testRecursiveSubSets() throws Exception {
+        int setSize = expensiveTests ? 1000 : 100;
+        Class cl = TreeSet.class;
+
+        NavigableSet<Integer> set = newSet(cl);
+        bs = new BitSet(setSize);
+
+        populate(set, setSize);
+        check(set,                 0, setSize - 1, true);
+        check(set.descendingSet(), 0, setSize - 1, false);
+
+        mutateSet(set, 0, setSize - 1);
+        check(set,                 0, setSize - 1, true);
+        check(set.descendingSet(), 0, setSize - 1, false);
+
+        bashSubSet(set.subSet(0, true, setSize, false),
+                   0, setSize - 1, true);
+    }
+
+    /**
+     * addAll is idempotent
+     */
+    public void testAddAll_idempotent() throws Exception {
+        Set x = populatedSet(SIZE);
+        Set y = new TreeSet(x);
+        y.addAll(x);
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    static NavigableSet<Integer> newSet(Class cl) throws Exception {
+        NavigableSet<Integer> result = (NavigableSet<Integer>) cl.newInstance();
+        assertEquals(0, result.size());
+        assertFalse(result.iterator().hasNext());
+        return result;
+    }
+
+    void populate(NavigableSet<Integer> set, int limit) {
+        for (int i = 0, n = 2 * limit / 3; i < n; i++) {
+            int element = rnd.nextInt(limit);
+            put(set, element);
+        }
+    }
+
+    void mutateSet(NavigableSet<Integer> set, int min, int max) {
+        int size = set.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(set, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (set.size() < size) {
+            int element = min + rnd.nextInt(rangeSize);
+            assertTrue(element >= min && element<= max);
+            put(set, element);
+        }
+    }
+
+    void mutateSubSet(NavigableSet<Integer> set, int min, int max) {
+        int size = set.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(set, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (set.size() < size) {
+            int element = min - 5 + rnd.nextInt(rangeSize + 10);
+            if (element >= min && element<= max) {
+                put(set, element);
+            } else {
+                try {
+                    set.add(element);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+            }
+        }
+    }
+
+    void put(NavigableSet<Integer> set, int element) {
+        if (set.add(element))
+            bs.set(element);
+    }
+
+    void remove(NavigableSet<Integer> set, int element) {
+        if (set.remove(element))
+            bs.clear(element);
+    }
+
+    void bashSubSet(NavigableSet<Integer> set,
+                    int min, int max, boolean ascending) {
+        check(set, min, max, ascending);
+        check(set.descendingSet(), min, max, !ascending);
+
+        mutateSubSet(set, min, max);
+        check(set, min, max, ascending);
+        check(set.descendingSet(), min, max, !ascending);
+
+        // Recurse
+        if (max - min < 2)
+            return;
+        int midPoint = (min + max) / 2;
+
+        // headSet - pick direction and endpoint inclusion randomly
+        boolean incl = rnd.nextBoolean();
+        NavigableSet<Integer> hm = set.headSet(midPoint, incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubSet(hm, min, midPoint - (incl ? 0 : 1), true);
+            else
+                bashSubSet(hm.descendingSet(), min, midPoint - (incl ? 0 : 1),
+                           false);
+        } else {
+            if (rnd.nextBoolean())
+                bashSubSet(hm, midPoint + (incl ? 0 : 1), max, false);
+            else
+                bashSubSet(hm.descendingSet(), midPoint + (incl ? 0 : 1), max,
+                           true);
+        }
+
+        // tailSet - pick direction and endpoint inclusion randomly
+        incl = rnd.nextBoolean();
+        NavigableSet<Integer> tm = set.tailSet(midPoint,incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubSet(tm, midPoint + (incl ? 0 : 1), max, true);
+            else
+                bashSubSet(tm.descendingSet(), midPoint + (incl ? 0 : 1), max,
+                           false);
+        } else {
+            if (rnd.nextBoolean()) {
+                bashSubSet(tm, min, midPoint - (incl ? 0 : 1), false);
+            } else {
+                bashSubSet(tm.descendingSet(), min, midPoint - (incl ? 0 : 1),
+                           true);
+            }
+        }
+
+        // subSet - pick direction and endpoint inclusion randomly
+        int rangeSize = max - min + 1;
+        int[] endpoints = new int[2];
+        endpoints[0] = min + rnd.nextInt(rangeSize);
+        endpoints[1] = min + rnd.nextInt(rangeSize);
+        Arrays.sort(endpoints);
+        boolean lowIncl = rnd.nextBoolean();
+        boolean highIncl = rnd.nextBoolean();
+        if (ascending) {
+            NavigableSet<Integer> sm = set.subSet(
+                endpoints[0], lowIncl, endpoints[1], highIncl);
+            if (rnd.nextBoolean())
+                bashSubSet(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+            else
+                bashSubSet(sm.descendingSet(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+        } else {
+            NavigableSet<Integer> sm = set.subSet(
+                endpoints[1], highIncl, endpoints[0], lowIncl);
+            if (rnd.nextBoolean())
+                bashSubSet(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+            else
+                bashSubSet(sm.descendingSet(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+        }
+    }
+
+    /**
+     * min and max are both inclusive.  If max < min, interval is empty.
+     */
+    void check(NavigableSet<Integer> set,
+                      final int min, final int max, final boolean ascending) {
+        class ReferenceSet {
+            int lower(int element) {
+                return ascending ?
+                    lowerAscending(element) : higherAscending(element);
+            }
+            int floor(int element) {
+                return ascending ?
+                    floorAscending(element) : ceilingAscending(element);
+            }
+            int ceiling(int element) {
+                return ascending ?
+                    ceilingAscending(element) : floorAscending(element);
+            }
+            int higher(int element) {
+                return ascending ?
+                    higherAscending(element) : lowerAscending(element);
+            }
+            int first() {
+                return ascending ? firstAscending() : lastAscending();
+            }
+            int last() {
+                return ascending ? lastAscending() : firstAscending();
+            }
+            int lowerAscending(int element) {
+                return floorAscending(element - 1);
+            }
+            int floorAscending(int element) {
+                if (element < min)
+                    return -1;
+                else if (element > max)
+                    element = max;
+
+                // BitSet should support this! Test would run much faster
+                while (element >= min) {
+                    if (bs.get(element))
+                        return element;
+                    element--;
+                }
+                return -1;
+            }
+            int ceilingAscending(int element) {
+                if (element < min)
+                    element = min;
+                else if (element > max)
+                    return -1;
+                int result = bs.nextSetBit(element);
+                return result > max ? -1 : result;
+            }
+            int higherAscending(int element) {
+                return ceilingAscending(element + 1);
+            }
+            private int firstAscending() {
+                int result = ceilingAscending(min);
+                return result > max ? -1 : result;
+            }
+            private int lastAscending() {
+                int result = floorAscending(max);
+                return result < min ? -1 : result;
+            }
+        }
+        ReferenceSet rs = new ReferenceSet();
+
+        // Test contents using containsElement
+        int size = 0;
+        for (int i = min; i <= max; i++) {
+            boolean bsContainsI = bs.get(i);
+            assertEquals(bsContainsI, set.contains(i));
+            if (bsContainsI)
+                size++;
+        }
+        assertEquals(size, set.size());
+
+        // Test contents using contains elementSet iterator
+        int size2 = 0;
+        int previousElement = -1;
+        for (int element : set) {
+            assertTrue(bs.get(element));
+            size2++;
+            assertTrue(previousElement < 0 || (ascending ?
+                element - previousElement > 0 : element - previousElement < 0));
+            previousElement = element;
+        }
+        assertEquals(size2, size);
+
+        // Test navigation ops
+        for (int element = min - 1; element <= max + 1; element++) {
+            assertEq(set.lower(element), rs.lower(element));
+            assertEq(set.floor(element), rs.floor(element));
+            assertEq(set.higher(element), rs.higher(element));
+            assertEq(set.ceiling(element), rs.ceiling(element));
+        }
+
+        // Test extrema
+        if (set.size() != 0) {
+            assertEq(set.first(), rs.first());
+            assertEq(set.last(), rs.last());
+        } else {
+            assertEq(rs.first(), -1);
+            assertEq(rs.last(),  -1);
+            try {
+                set.first();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                set.last();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+        }
+    }
+
+    static void assertEq(Integer i, int j) {
+        if (i == null)
+            assertEquals(j, -1);
+        else
+            assertEquals((int) i, j);
+    }
+
+    static boolean eq(Integer i, int j) {
+        return i == null ? j == -1 : i == j;
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/TreeSubMapTest.java b/jsr166-tests/src/test/java/jsr166/TreeSubMapTest.java
new file mode 100644
index 0000000..17201f3
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/TreeSubMapTest.java
@@ -0,0 +1,1097 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.*;
+
+public class TreeSubMapTest extends JSR166TestCase {
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static NavigableMap map5() {
+        TreeMap map = new TreeMap();
+        assertTrue(map.isEmpty());
+        map.put(zero, "Z");
+        map.put(one, "A");
+        map.put(five, "E");
+        map.put(three, "C");
+        map.put(two, "B");
+        map.put(four, "D");
+        map.put(seven, "F");
+        assertFalse(map.isEmpty());
+        assertEquals(7, map.size());
+        return map.subMap(one, true, seven, false);
+    }
+
+    private static NavigableMap map0() {
+        TreeMap map = new TreeMap();
+        assertTrue(map.isEmpty());
+        return map.tailMap(one, true);
+    }
+
+    /**
+     * Returns a new map from Integers -5 to -1 to Strings "A"-"E".
+     */
+    private static NavigableMap dmap5() {
+        TreeMap map = new TreeMap();
+        assertTrue(map.isEmpty());
+        map.put(m1, "A");
+        map.put(m5, "E");
+        map.put(m3, "C");
+        map.put(m2, "B");
+        map.put(m4, "D");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map.descendingMap();
+    }
+
+    private static NavigableMap dmap0() {
+        TreeMap map = new TreeMap();
+        assertTrue(map.isEmpty());
+        return map;
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testClear() {
+        NavigableMap map = map5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testEquals() {
+        NavigableMap map1 = map5();
+        NavigableMap map2 = map5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testContainsKey() {
+        NavigableMap map = map5();
+        assertTrue(map.containsKey(one));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testContainsValue() {
+        NavigableMap map = map5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testGet() {
+        NavigableMap map = map5();
+        assertEquals("A", (String)map.get(one));
+        NavigableMap empty = map0();
+        assertNull(empty.get(one));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testIsEmpty() {
+        NavigableMap empty = map0();
+        NavigableMap map = map5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testFirstKey() {
+        NavigableMap map = map5();
+        assertEquals(one, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testLastKey() {
+        NavigableMap map = map5();
+        assertEquals(five, map.lastKey());
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testKeySet() {
+        NavigableMap map = map5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(one));
+        assertTrue(s.contains(two));
+        assertTrue(s.contains(three));
+        assertTrue(s.contains(four));
+        assertTrue(s.contains(five));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testKeySetOrder() {
+        NavigableMap map = map5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+        }
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testValues() {
+        NavigableMap map = map5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testEntrySet() {
+        NavigableMap map = map5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testPutAll() {
+        NavigableMap empty = map0();
+        NavigableMap map = map5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(one));
+        assertTrue(empty.containsKey(two));
+        assertTrue(empty.containsKey(three));
+        assertTrue(empty.containsKey(four));
+        assertTrue(empty.containsKey(five));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testRemove() {
+        NavigableMap map = map5();
+        map.remove(five);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testLowerEntry() {
+        NavigableMap map = map5();
+        Map.Entry e1 = map.lowerEntry(three);
+        assertEquals(two, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(one);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testHigherEntry() {
+        NavigableMap map = map5();
+        Map.Entry e1 = map.higherEntry(three);
+        assertEquals(four, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(five);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testFloorEntry() {
+        NavigableMap map = map5();
+        Map.Entry e1 = map.floorEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(one);
+        assertEquals(one, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testCeilingEntry() {
+        NavigableMap map = map5();
+        Map.Entry e1 = map.ceilingEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(five);
+        assertEquals(five, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testPollFirstEntry() {
+        NavigableMap map = map5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(two, e.getKey());
+        map.put(one, "A");
+        e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(three, e.getKey());
+        map.remove(four);
+        e = map.pollFirstEntry();
+        assertEquals(five, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        assertTrue(map.isEmpty());
+        Map.Entry f = map.firstEntry();
+        assertNull(f);
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testPollLastEntry() {
+        NavigableMap map = map5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(four, e.getKey());
+        map.put(five, "E");
+        e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(three, e.getKey());
+        map.remove(two);
+        e = map.pollLastEntry();
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testSize() {
+        NavigableMap map = map5();
+        NavigableMap empty = map0();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        NavigableMap map = map5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception tests
+
+    /**
+     * get(null) of nonempty map throws NPE
+     */
+    public void testGet_NullPointerException() {
+        try {
+            NavigableMap c = map5();
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) of nonempty map throws NPE
+     */
+    public void testContainsKey_NullPointerException() {
+        try {
+            NavigableMap c = map5();
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testPut1_NullPointerException() {
+        try {
+            NavigableMap c = map5();
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE
+     */
+    public void testRemove1_NullPointerException() {
+        try {
+            NavigableMap c = map5();
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testSerialization() throws Exception {
+        NavigableMap x = map5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testSubMapContents() {
+        NavigableMap map = map5();
+        SortedMap sm = map.subMap(two, four);
+        assertEquals(two, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals("C", sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testSubMapContents2() {
+        NavigableMap map = map5();
+        SortedMap sm = map.subMap(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.firstKey());
+        assertEquals(two, sm.lastKey());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertFalse(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(three), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testHeadMapContents() {
+        NavigableMap map = map5();
+        SortedMap sm = map.headMap(four);
+        assertTrue(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(four, map.firstKey());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testTailMapContents() {
+        NavigableMap map = map5();
+        SortedMap sm = map.tailMap(two);
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertTrue(sm.containsKey(four));
+        assertTrue(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(two, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(three, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(four, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        SortedMap ssm = sm.tailMap(four);
+        assertEquals(four, ssm.firstKey());
+        assertEquals(five, ssm.lastKey());
+        assertEquals("D", ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testDescendingClear() {
+        NavigableMap map = dmap5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testDescendingEquals() {
+        NavigableMap map1 = dmap5();
+        NavigableMap map2 = dmap5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testDescendingContainsKey() {
+        NavigableMap map = dmap5();
+        assertTrue(map.containsKey(m1));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testDescendingContainsValue() {
+        NavigableMap map = dmap5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testDescendingGet() {
+        NavigableMap map = dmap5();
+        assertEquals("A", (String)map.get(m1));
+        NavigableMap empty = dmap0();
+        assertNull(empty.get(m1));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testDescendingIsEmpty() {
+        NavigableMap empty = dmap0();
+        NavigableMap map = dmap5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testDescendingFirstKey() {
+        NavigableMap map = dmap5();
+        assertEquals(m1, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testDescendingLastKey() {
+        NavigableMap map = dmap5();
+        assertEquals(m5, map.lastKey());
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testDescendingKeySet() {
+        NavigableMap map = dmap5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(m1));
+        assertTrue(s.contains(m2));
+        assertTrue(s.contains(m3));
+        assertTrue(s.contains(m4));
+        assertTrue(s.contains(m5));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testDescendingKeySetOrder() {
+        NavigableMap map = dmap5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, m1);
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+        }
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testDescendingValues() {
+        NavigableMap map = dmap5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testDescendingAscendingKeySetToArray() {
+        NavigableMap map = dmap5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * descendingkeySet.toArray returns contains all keys
+     */
+    public void testDescendingDescendingKeySetToArray() {
+        NavigableMap map = dmap5();
+        Set s = map.descendingKeySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * Values.toArray contains all values
+     */
+    public void testDescendingValuesToArray() {
+        NavigableMap map = dmap5();
+        Collection v = map.values();
+        Object[] ar = v.toArray();
+        ArrayList s = new ArrayList(Arrays.asList(ar));
+        assertEquals(5, ar.length);
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testDescendingEntrySet() {
+        NavigableMap map = dmap5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(m1) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(m2) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(m3) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(m4) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(m5) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testDescendingPutAll() {
+        NavigableMap empty = dmap0();
+        NavigableMap map = dmap5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(m1));
+        assertTrue(empty.containsKey(m2));
+        assertTrue(empty.containsKey(m3));
+        assertTrue(empty.containsKey(m4));
+        assertTrue(empty.containsKey(m5));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testDescendingRemove() {
+        NavigableMap map = dmap5();
+        map.remove(m5);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(m5));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testDescendingLowerEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e1 = map.lowerEntry(m3);
+        assertEquals(m2, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(m6);
+        assertEquals(m5, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(m1);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testDescendingHigherEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e1 = map.higherEntry(m3);
+        assertEquals(m4, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(m1, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(m5);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testDescendingFloorEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e1 = map.floorEntry(m3);
+        assertEquals(m3, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(m6);
+        assertEquals(m5, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(m1);
+        assertEquals(m1, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testDescendingCeilingEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e1 = map.ceilingEntry(m3);
+        assertEquals(m3, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(m1, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(m5);
+        assertEquals(m5, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testDescendingPollFirstEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(m1, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(m2, e.getKey());
+        map.put(m1, "A");
+        e = map.pollFirstEntry();
+        assertEquals(m1, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(m3, e.getKey());
+        map.remove(m4);
+        e = map.pollFirstEntry();
+        assertEquals(m5, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testDescendingPollLastEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(m4, e.getKey());
+        map.put(m5, "E");
+        e = map.pollLastEntry();
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(m3, e.getKey());
+        map.remove(m2);
+        e = map.pollLastEntry();
+        assertEquals(m1, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testDescendingSize() {
+        NavigableMap map = dmap5();
+        NavigableMap empty = dmap0();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testDescendingToString() {
+        NavigableMap map = dmap5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception testDescendings
+
+    /**
+     * get(null) of nonempty map throws NPE
+     */
+    public void testDescendingGet_NullPointerException() {
+        try {
+            NavigableMap c = dmap5();
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testDescendingPut1_NullPointerException() {
+        try {
+            NavigableMap c = dmap5();
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testDescendingSerialization() throws Exception {
+        NavigableMap x = dmap5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testDescendingSubMapContents() {
+        NavigableMap map = dmap5();
+        SortedMap sm = map.subMap(m2, m4);
+        assertEquals(m2, sm.firstKey());
+        assertEquals(m3, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(m2));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(m3, sm.firstKey());
+        assertEquals(m3, sm.lastKey());
+        assertEquals("C", sm.remove(m3));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testDescendingSubMapContents2() {
+        NavigableMap map = dmap5();
+        SortedMap sm = map.subMap(m2, m3);
+        assertEquals(1, sm.size());
+        assertEquals(m2, sm.firstKey());
+        assertEquals(m2, sm.lastKey());
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertFalse(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(m2));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(m3), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testDescendingHeadMapContents() {
+        NavigableMap map = dmap5();
+        SortedMap sm = map.headMap(m4);
+        assertTrue(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m1, k);
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(m4, map.firstKey());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testDescendingTailMapContents() {
+        NavigableMap map = dmap5();
+        SortedMap sm = map.tailMap(m2);
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertTrue(sm.containsKey(m4));
+        assertTrue(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        k = (Integer)(i.next());
+        assertEquals(m4, k);
+        k = (Integer)(i.next());
+        assertEquals(m5, k);
+        assertFalse(i.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(m2, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m3, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m4, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        SortedMap ssm = sm.tailMap(m4);
+        assertEquals(m4, ssm.firstKey());
+        assertEquals(m5, ssm.lastKey());
+        assertEquals("D", ssm.remove(m4));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/TreeSubSetTest.java b/jsr166-tests/src/test/java/jsr166/TreeSubSetTest.java
new file mode 100644
index 0000000..ba61748
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/TreeSubSetTest.java
@@ -0,0 +1,1116 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import junit.framework.*;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NavigableSet;
+import java.util.SortedSet;
+import java.util.Set;
+import java.util.TreeSet;
+
+public class TreeSubSetTest extends JSR166TestCase {
+
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * Returns a new set of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private NavigableSet<Integer> populatedSet(int n) {
+        TreeSet<Integer> q = new TreeSet<Integer>();
+        assertTrue(q.isEmpty());
+
+        for (int i = n-1; i >= 0; i-=2)
+            assertTrue(q.add(new Integer(i)));
+        for (int i = (n & 1); i < n; i+=2)
+            assertTrue(q.add(new Integer(i)));
+        assertTrue(q.add(new Integer(-n)));
+        assertTrue(q.add(new Integer(n)));
+        NavigableSet s = q.subSet(new Integer(0), true, new Integer(n), false);
+        assertFalse(s.isEmpty());
+        assertEquals(n, s.size());
+        return s;
+    }
+
+    /**
+     * Returns a new set of first 5 ints.
+     */
+    private NavigableSet set5() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        q.add(four);
+        q.add(five);
+        q.add(zero);
+        q.add(seven);
+        NavigableSet s = q.subSet(one, true, seven, false);
+        assertEquals(5, s.size());
+        return s;
+    }
+
+    private NavigableSet dset5() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.isEmpty());
+        q.add(m1);
+        q.add(m2);
+        q.add(m3);
+        q.add(m4);
+        q.add(m5);
+        NavigableSet s = q.descendingSet();
+        assertEquals(5, s.size());
+        return s;
+    }
+
+    private static NavigableSet set0() {
+        TreeSet set = new TreeSet();
+        assertTrue(set.isEmpty());
+        return set.tailSet(m1, false);
+    }
+
+    private static NavigableSet dset0() {
+        TreeSet set = new TreeSet();
+        assertTrue(set.isEmpty());
+        return set;
+    }
+
+    /**
+     * A new set has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(0, set0().size());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        NavigableSet q = set0();
+        assertTrue(q.isEmpty());
+        assertTrue(q.add(new Integer(1)));
+        assertFalse(q.isEmpty());
+        assertTrue(q.add(new Integer(2)));
+        q.pollFirst();
+        q.pollFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        try {
+            NavigableSet q = set0();
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testAdd() {
+        NavigableSet q = set0();
+        assertTrue(q.add(six));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testAddDup() {
+        NavigableSet q = set0();
+        assertTrue(q.add(six));
+        assertFalse(q.add(six));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testAddNonComparable() {
+        try {
+            NavigableSet q = set0();
+            q.add(new Object());
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        try {
+            NavigableSet q = set0();
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        try {
+            NavigableSet q = set0();
+            Integer[] ints = new Integer[SIZE];
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        try {
+            NavigableSet q = set0();
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i+SIZE);
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE-1- i);
+        NavigableSet q = set0();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.pollFirst());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i-1));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i+1));
+            assertFalse(q.contains(i+1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        NavigableSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertTrue(q.add(new Integer(1)));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = set0();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE-i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            NavigableSet q = populatedSet(SIZE);
+            NavigableSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE-i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer I = (Integer)(p.pollFirst());
+                assertFalse(q.contains(I));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testLower() {
+        NavigableSet q = set5();
+        Object e1 = q.lower(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lower(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lower(one);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testHigher() {
+        NavigableSet q = set5();
+        Object e1 = q.higher(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higher(five);
+        assertNull(e3);
+
+        Object e4 = q.higher(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testFloor() {
+        NavigableSet q = set5();
+        Object e1 = q.floor(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floor(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floor(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testCeiling() {
+        NavigableSet q = set5();
+        Object e1 = q.ceiling(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceiling(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceiling(six);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements in sorted order
+     */
+    public void testToArray() {
+        NavigableSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements in sorted order
+     */
+    public void testToArray2() {
+        NavigableSet<Integer> q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        NavigableSet q = populatedSet(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testEmptyIterator() {
+        NavigableSet q = set0();
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(0, i);
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final NavigableSet q = set0();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(2, it.next());
+        assertEquals(3, it.next());
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        NavigableSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testSerialization() throws Exception {
+        NavigableSet x = populatedSet(SIZE);
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testSubSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.subSet(two, four);
+        assertEquals(two, sm.first());
+        assertEquals(three, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.first());
+        assertEquals(three, sm.last());
+        assertTrue(sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testSubSetContents2() {
+        NavigableSet set = set5();
+        SortedSet sm = set.subSet(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.first());
+        assertEquals(two, sm.last());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertFalse(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(three));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testHeadSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.headSet(four);
+        assertTrue(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(four, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testTailSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.tailSet(two);
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertTrue(sm.contains(four));
+        assertTrue(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(four);
+        assertEquals(four, ssm.first());
+        assertEquals(five, ssm.last());
+        assertTrue(ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testDescendingSize() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE-i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testDescendingAdd() {
+        NavigableSet q = dset0();
+        assertTrue(q.add(m6));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testDescendingAddDup() {
+        NavigableSet q = dset0();
+        assertTrue(q.add(m6));
+        assertFalse(q.add(m6));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testDescendingAddNonComparable() {
+        try {
+            NavigableSet q = dset0();
+            q.add(new Object());
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testDescendingAddAll1() {
+        try {
+            NavigableSet q = dset0();
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testDescendingAddAll2() {
+        try {
+            NavigableSet q = dset0();
+            Integer[] ints = new Integer[SIZE];
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testDescendingAddAll3() {
+        try {
+            NavigableSet q = dset0();
+            Integer[] ints = new Integer[SIZE];
+            for (int i = 0; i < SIZE-1; ++i)
+                ints[i] = new Integer(i+SIZE);
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testDescendingAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE-1- i);
+        NavigableSet q = dset0();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.pollFirst());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testDescendingPoll() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testDescendingRemoveElement() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i+=2) {
+            assertTrue(q.remove(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i+=2) {
+            assertTrue(q.remove(new Integer(i)));
+            assertFalse(q.remove(new Integer(i+1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testDescendingContains() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testDescendingClear() {
+        NavigableSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertTrue(q.add(new Integer(1)));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testDescendingContainsAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = dset0();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testDescendingRetainAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE-i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testDescendingRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            NavigableSet q = populatedSet(SIZE);
+            NavigableSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE-i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer I = (Integer)(p.pollFirst());
+                assertFalse(q.contains(I));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testDescendingLower() {
+        NavigableSet q = dset5();
+        Object e1 = q.lower(m3);
+        assertEquals(m2, e1);
+
+        Object e2 = q.lower(m6);
+        assertEquals(m5, e2);
+
+        Object e3 = q.lower(m1);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testDescendingHigher() {
+        NavigableSet q = dset5();
+        Object e1 = q.higher(m3);
+        assertEquals(m4, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(m1, e2);
+
+        Object e3 = q.higher(m5);
+        assertNull(e3);
+
+        Object e4 = q.higher(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testDescendingFloor() {
+        NavigableSet q = dset5();
+        Object e1 = q.floor(m3);
+        assertEquals(m3, e1);
+
+        Object e2 = q.floor(m6);
+        assertEquals(m5, e2);
+
+        Object e3 = q.floor(m1);
+        assertEquals(m1, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testDescendingCeiling() {
+        NavigableSet q = dset5();
+        Object e1 = q.ceiling(m3);
+        assertEquals(m3, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(m1, e2);
+
+        Object e3 = q.ceiling(m5);
+        assertEquals(m5, e3);
+
+        Object e4 = q.ceiling(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements
+     */
+    public void testDescendingToArray() {
+        NavigableSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        Arrays.sort(o);
+        for (int i = 0; i < o.length; i++)
+            assertEquals(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements
+     */
+    public void testDescendingToArray2() {
+        NavigableSet q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        assertSame(ints, q.toArray(ints));
+        Arrays.sort(ints);
+        for (int i = 0; i < ints.length; i++)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        NavigableSet q = populatedSet(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testDescendingEmptyIterator() {
+        NavigableSet q = dset0();
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(0, i);
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final NavigableSet q = dset0();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(2, it.next());
+        assertEquals(3, it.next());
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testDescendingToString() {
+        NavigableSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testDescendingSerialization() throws Exception {
+        NavigableSet x = dset5();
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testDescendingSubSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.subSet(m2, m4);
+        assertEquals(m2, sm.first());
+        assertEquals(m3, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(m2));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(m3, sm.first());
+        assertEquals(m3, sm.last());
+        assertTrue(sm.remove(m3));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testDescendingSubSetContents2() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.subSet(m2, m3);
+        assertEquals(1, sm.size());
+        assertEquals(m2, sm.first());
+        assertEquals(m2, sm.last());
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertFalse(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(m2));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(m3));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testDescendingHeadSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.headSet(m4);
+        assertTrue(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m1, k);
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(m4, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testDescendingTailSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.tailSet(m2);
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertTrue(sm.contains(m4));
+        assertTrue(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        k = (Integer)(i.next());
+        assertEquals(m4, k);
+        k = (Integer)(i.next());
+        assertEquals(m5, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(m4);
+        assertEquals(m4, ssm.first());
+        assertEquals(m5, ssm.last());
+        assertTrue(ssm.remove(m4));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * addAll is idempotent
+     */
+    public void testAddAll_idempotent() throws Exception {
+        Set x = populatedSet(SIZE);
+        Set y = new TreeSet(x);
+        y.addAll(x);
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+}
diff --git a/libdvm/src/main/java/java/lang/Class.java b/libdvm/src/main/java/java/lang/Class.java
index 2f26688..c8064cb 100644
--- a/libdvm/src/main/java/java/lang/Class.java
+++ b/libdvm/src/main/java/java/lang/Class.java
@@ -54,13 +54,14 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import libcore.util.CollectionUtils;
 import libcore.util.EmptyArray;
 import org.apache.harmony.kernel.vm.StringUtils;
 import libcore.reflect.AnnotationAccess;
 import libcore.reflect.GenericSignatureParser;
 import libcore.reflect.Types;
-
+import libcore.util.BasicLruCache;
 /**
  * The in-memory representation of a Java class. This representation serves as
  * the starting point for querying class-related information, a process usually
@@ -779,9 +780,17 @@
      * void} then an empty array is returned.
      */
     public Type[] getGenericInterfaces() {
-        GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
-        parser.parseForClass(this, getSignatureAttribute());
-        return Types.getClonedTypeArray(parser.interfaceTypes);
+        Type[] result;
+        synchronized (Caches.genericInterfaces) {
+            result = Caches.genericInterfaces.get(this);
+            if (result == null) {
+                GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
+                parser.parseForClass(this, getSignatureAttribute());
+                result = Types.getClonedTypeArray(parser.interfaceTypes);
+                Caches.genericInterfaces.put(this, result);
+            }
+       }
+       return result;
     }
 
     /**
@@ -1262,4 +1271,9 @@
         return AnnotationAccess.typeIndexToAnnotationDirectoryOffset(getDex(), getTypeIndex());
     }
 
+    private static class Caches {
+        private static final BasicLruCache<Class, Type[]> genericInterfaces
+            = new BasicLruCache<Class, Type[]>(50);
+    }
+
 }
diff --git a/luni/src/main/java/java/io/BufferedInputStream.java b/luni/src/main/java/java/io/BufferedInputStream.java
index 5a810e7..f579e49 100644
--- a/luni/src/main/java/java/io/BufferedInputStream.java
+++ b/luni/src/main/java/java/io/BufferedInputStream.java
@@ -325,7 +325,7 @@
         if (buf == null) {
             throw new IOException("Stream is closed");
         }
-        if (-1 == markpos) {
+        if (markpos == -1) {
             throw new IOException("Mark has been invalidated.");
         }
         pos = markpos;
diff --git a/luni/src/main/java/java/text/spi/BreakIteratorProvider.java b/luni/src/main/java/java/text/spi/BreakIteratorProvider.java
deleted file mode 100644
index a28bc53..0000000
--- a/luni/src/main/java/java/text/spi/BreakIteratorProvider.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 java.text.spi;
-
-import java.text.BreakIterator;
-import java.util.Locale;
-import java.util.spi.LocaleServiceProvider;
-
-/**
- * This abstract class should be extended by service providers that provide
- * instances of {@code BreakIterator}.
- * <p>Note that Android does not support user-supplied locale service providers.
- * @since 1.6
- * @hide
- */
-public abstract class BreakIteratorProvider extends LocaleServiceProvider {
-    /**
-     * Default constructor, for use by subclasses.
-     */
-    protected BreakIteratorProvider() {
-        // Do nothing.
-    }
-
-    /**
-     * Returns an instance of {@code BreakIterator} for word breaks in the
-     * given locale.
-     *
-     * @param locale the locale
-     * @return an instance of {@code BreakIterator}
-     * @throws NullPointerException if {@code locale == null}
-     * @throws IllegalArgumentException
-     *             if locale isn't one of the locales returned from
-     *             getAvailableLocales().
-     */
-    public abstract BreakIterator getWordInstance(Locale locale);
-
-    /**
-     * Returns an instance of {@code BreakIterator} for line breaks in the
-     * given locale.
-     *
-     * @param locale the locale
-     * @return an instance of {@code BreakIterator}
-     * @throws NullPointerException if {@code locale == null}
-     * @throws IllegalArgumentException
-     *             if locale isn't one of the locales returned from
-     *             getAvailableLocales().
-     */
-    public abstract BreakIterator getLineInstance(Locale locale);
-
-    /**
-     * Returns an instance of {@code BreakIterator} for character breaks in the
-     * given locale.
-     *
-     * @param locale the locale
-     * @return an instance of {@code BreakIterator}
-     * @throws NullPointerException if {@code locale == null}
-     * @throws IllegalArgumentException
-     *             if locale isn't one of the locales returned from
-     *             getAvailableLocales().
-     */
-    public abstract BreakIterator getCharacterInstance(Locale locale);
-
-    /**
-     * Returns an instance of {@code BreakIterator} for sentence breaks in the
-     * given locale.
-     *
-     * @param locale the locale
-     * @return an instance of {@code BreakIterator}
-     * @throws NullPointerException if {@code locale == null}
-     * @throws IllegalArgumentException
-     *             if locale isn't one of the locales returned from
-     *             getAvailableLocales().
-     */
-    public abstract BreakIterator getSentenceInstance(Locale locale);
-}
diff --git a/luni/src/main/java/java/text/spi/CollatorProvider.java b/luni/src/main/java/java/text/spi/CollatorProvider.java
deleted file mode 100644
index 0862432..0000000
--- a/luni/src/main/java/java/text/spi/CollatorProvider.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 java.text.spi;
-
-import java.text.Collator;
-import java.util.Locale;
-import java.util.spi.LocaleServiceProvider;
-
-/**
- * This abstract class should be extended by service providers which provide
- * instances of {@code Collator}.
- * <p>Note that Android does not support user-supplied locale service providers.
- * @since 1.6
- * @hide
- */
-public abstract class CollatorProvider extends LocaleServiceProvider {
-    /**
-     * Default constructor, for use by subclasses.
-     */
-    protected CollatorProvider() {
-        // Do nothing.
-    }
-
-    /**
-     * Returns an instance of {@code Collator} for the given locale.
-     *
-     * @param locale the locale
-     * @return an instance of {@code Collator}
-     * @throws NullPointerException if {@code locale == null}
-     * @throws IllegalArgumentException
-     *             if locale isn't one of the locales returned from
-     *             getAvailableLocales().
-     */
-    public abstract Collator getInstance(Locale locale);
-}
diff --git a/luni/src/main/java/java/text/spi/DateFormatProvider.java b/luni/src/main/java/java/text/spi/DateFormatProvider.java
deleted file mode 100644
index 59fefed..0000000
--- a/luni/src/main/java/java/text/spi/DateFormatProvider.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 java.text.spi;
-
-import java.text.DateFormat;
-import java.util.Locale;
-import java.util.spi.LocaleServiceProvider;
-
-/**
- * This abstract class should be extended by service providers that provide
- * instances of {@code DateFormat}.
- * <p>Note that Android does not support user-supplied locale service providers.
- * @since 1.6
- * @hide
- */
-public abstract class DateFormatProvider extends LocaleServiceProvider {
-    /**
-     * Default constructor, for use by subclasses.
-     */
-    protected DateFormatProvider() {
-        // Do nothing.
-    }
-
-    /**
-     * Returns an instance of {@code DateFormat} that formats times
-     * in the given style for the given locale.
-     *
-     * @param style the given time formatting style.
-     * @param locale the locale
-     * @return an instance of {@code DateFormat}
-     * @throws NullPointerException if {@code locale == null}
-     * @throws IllegalArgumentException
-     *             if locale isn't one of the locales returned from
-     *             getAvailableLocales().
-     */
-    public abstract DateFormat getTimeInstance(int style, Locale locale);
-
-    /**
-     * Returns an instance of {@code DateFormat} that formats dates
-     * in the given style for the given locale.
-     *
-     * @param style the given date formatting style.
-     * @param locale the locale
-     * @return an instance of {@code DateFormat}
-     * @throws NullPointerException if {@code locale == null}
-     * @throws IllegalArgumentException
-     *             if locale isn't one of the locales returned from
-     *             getAvailableLocales().
-     */
-    public abstract DateFormat getDateInstance(int style, Locale locale);
-
-    /**
-     * Returns an instance of {@code DateFormat} that formats dates and times
-     * in the given style for the given locale.
-     *
-     * @param dateStyle the given date formatting style.
-     * @param timeStyle the given time formatting style.
-     * @param locale the locale
-     * @return an instance of {@code DateFormat}
-     * @throws NullPointerException if {@code locale == null}
-     * @throws IllegalArgumentException
-     *             if locale isn't one of the locales returned from
-     *             getAvailableLocales().
-     */
-    public abstract DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale locale);
-}
diff --git a/luni/src/main/java/java/text/spi/DateFormatSymbolsProvider.java b/luni/src/main/java/java/text/spi/DateFormatSymbolsProvider.java
deleted file mode 100644
index cb34b71..0000000
--- a/luni/src/main/java/java/text/spi/DateFormatSymbolsProvider.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 java.text.spi;
-
-import java.text.DateFormatSymbols;
-import java.util.Locale;
-import java.util.spi.LocaleServiceProvider;
-
-/**
- * This abstract class should be extended by service providers that provide
- * instances of {@code DateFormatSymbols}.
- * <p>Note that Android does not support user-supplied locale service providers.
- * @since 1.6
- * @hide
- */
-public abstract class DateFormatSymbolsProvider extends LocaleServiceProvider {
-    /**
-     * Default constructor, for use by subclasses.
-     */
-    protected DateFormatSymbolsProvider() {
-        // Do nothing.
-    }
-
-    /**
-     * Returns an instance of {@code DateFormatSymbols} for the given locale.
-     *
-     * @param locale the locale
-     * @return an instance of {@code DateFormatSymbols}
-     * @throws NullPointerException if {@code locale == null}
-     * @throws IllegalArgumentException
-     *             if locale isn't one of the locales returned from
-     *             getAvailableLocales().
-     */
-    public abstract DateFormatSymbols getInstance(Locale locale);
-}
diff --git a/luni/src/main/java/java/text/spi/DecimalFormatSymbolsProvider.java b/luni/src/main/java/java/text/spi/DecimalFormatSymbolsProvider.java
deleted file mode 100644
index f9f5e7d..0000000
--- a/luni/src/main/java/java/text/spi/DecimalFormatSymbolsProvider.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 java.text.spi;
-
-import java.text.DecimalFormatSymbols;
-import java.util.Locale;
-import java.util.spi.LocaleServiceProvider;
-
-/**
- * This abstract class should be extended by service providers that provide
- * instances of {@code DecimalFormatSymbols}.
- * <p>Note that Android does not support user-supplied locale service providers.
- * @since 1.6
- * @hide
- */
-public abstract class DecimalFormatSymbolsProvider extends LocaleServiceProvider {
-    /**
-     * Default constructor, for use by subclasses.
-     */
-    protected DecimalFormatSymbolsProvider() {
-        // Do nothing.
-    }
-
-    /**
-     * Returns an instance of {@code DecimalFormatSymbols} for the given locale.
-     *
-     * @param locale the locale
-     * @return an instance of {@code DecimalFormatSymbols}
-     * @throws NullPointerException if {@code locale == null}
-     * @throws IllegalArgumentException
-     *             if locale isn't one of the locales returned from
-     *             getAvailableLocales().
-     */
-    public abstract DecimalFormatSymbols getInstance(Locale locale);
-
-}
diff --git a/luni/src/main/java/java/text/spi/NumberFormatProvider.java b/luni/src/main/java/java/text/spi/NumberFormatProvider.java
deleted file mode 100644
index c889f78..0000000
--- a/luni/src/main/java/java/text/spi/NumberFormatProvider.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 java.text.spi;
-
-import java.text.NumberFormat;
-import java.util.Locale;
-import java.util.spi.LocaleServiceProvider;
-
-/**
- * This abstract class should be extended by service providers that provide
- * {@code NumberFormat} instances.
- * <p>Note that Android does not support user-supplied locale service providers.
- * @since 1.6
- * @hide
- */
-public abstract class NumberFormatProvider extends LocaleServiceProvider {
-    /**
-     * Default constructor, for use by subclasses.
-     */
-    protected NumberFormatProvider() {
-        // Do nothing.
-    }
-
-    /**
-     * Returns an instance of {@code NumberFormat} that formats
-     * monetary values for the given locale.
-     *
-     * @param locale the locale
-     * @return an instance of {@code NumberFormat}
-     * @throws NullPointerException if {@code locale == null}
-     * @throws IllegalArgumentException
-     *             if locale isn't one of the locales returned from
-     *             getAvailableLocales().
-     */
-    public abstract NumberFormat getCurrencyInstance(Locale locale);
-
-    /**
-     * Returns an instance of {@code NumberFormat} that formats
-     * integer values for the given locale. The returned {@code NumberFormat}
-     * is configured to round floating point numbers to the nearest integer
-     * using half-even rounding mode for formatting, and to parse only the
-     * integer part of an input string.
-     *
-     * @param locale the locale
-     * @return an instance of {@code NumberFormat}
-     * @throws NullPointerException if {@code locale == null}
-     * @throws IllegalArgumentException
-     *             if locale isn't one of the locales returned from
-     *             getAvailableLocales().
-     */
-    public abstract NumberFormat getIntegerInstance(Locale locale);
-
-    /**
-     * Returns an instance of {@code NumberFormat} class for general
-     * use in the given locale.
-     *
-     * @param locale the locale
-     * @return an instance of {@code NumberFormat}
-     * @throws NullPointerException if {@code locale == null}
-     * @throws IllegalArgumentException
-     *             if locale isn't one of the locales returned from
-     *             getAvailableLocales().
-     */
-    public abstract NumberFormat getNumberInstance(Locale locale);
-
-    /**
-     * Returns an instance of {@code NumberFormat} class that formats
-     * percentage values for the given locale.
-     *
-     * @param locale the locale
-     * @return an instance of {@code NumberFormat}
-     * @throws NullPointerException if {@code locale == null}
-     * @throws IllegalArgumentException
-     *             if locale isn't one of the locales returned from
-     *             getAvailableLocales().
-     */
-    public abstract NumberFormat getPercentInstance(Locale locale);
-}
diff --git a/luni/src/main/java/java/util/EnumMap.java b/luni/src/main/java/java/util/EnumMap.java
index 827fb51..dfacb46 100644
--- a/luni/src/main/java/java/util/EnumMap.java
+++ b/luni/src/main/java/java/util/EnumMap.java
@@ -174,7 +174,7 @@
         @Override
         @SuppressWarnings("unchecked")
         public String toString() {
-            if (-1 == prePosition) {
+            if (prePosition == -1) {
                 return super.toString();
             }
             return type.get(
@@ -183,7 +183,7 @@
         }
 
         private void checkStatus() {
-            if (-1 == prePosition) {
+            if (prePosition == -1) {
                 throw new IllegalStateException();
             }
         }
diff --git a/luni/src/main/java/java/util/Random.java b/luni/src/main/java/java/util/Random.java
index 4a67244..303e49f 100644
--- a/luni/src/main/java/java/util/Random.java
+++ b/luni/src/main/java/java/util/Random.java
@@ -85,6 +85,9 @@
      * Volume 2: Seminumerical Algorithms</i>, section 3.2.1.
      *
      * <p>Most applications will want to use one of this class' convenience methods instead.
+     *
+     * <p>Subclasses only need to override this method to alter the behavior
+     * of all the public methods.
      */
     protected synchronized int next(int bits) {
         seed = (seed * multiplier + 0xbL) & ((1L << 48) - 1);
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
index c85a5cc..515cc38 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
@@ -5,865 +5,729 @@
  */
 
 package java.util.concurrent;
-import java.util.concurrent.locks.*;
-import java.util.*;
+
+import java.io.ObjectStreamField;
 import java.io.Serializable;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.ConcurrentModificationException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.LockSupport;
+import java.util.concurrent.locks.ReentrantLock;
 
 // BEGIN android-note
 // removed link to collections framework docs
+// removed links to hidden api
 // END android-note
 
 /**
  * A hash table supporting full concurrency of retrievals and
- * adjustable expected concurrency for updates. This class obeys the
+ * high expected concurrency for updates. This class obeys the
  * same functional specification as {@link java.util.Hashtable}, and
  * includes versions of methods corresponding to each method of
- * <tt>Hashtable</tt>. However, even though all operations are
+ * {@code Hashtable}. However, even though all operations are
  * thread-safe, retrieval operations do <em>not</em> entail locking,
  * and there is <em>not</em> any support for locking the entire table
  * in a way that prevents all access.  This class is fully
- * interoperable with <tt>Hashtable</tt> in programs that rely on its
+ * interoperable with {@code Hashtable} in programs that rely on its
  * thread safety but not on its synchronization details.
  *
- * <p> Retrieval operations (including <tt>get</tt>) generally do not
- * block, so may overlap with update operations (including
- * <tt>put</tt> and <tt>remove</tt>). Retrievals reflect the results
- * of the most recently <em>completed</em> update operations holding
- * upon their onset.  For aggregate operations such as <tt>putAll</tt>
- * and <tt>clear</tt>, concurrent retrievals may reflect insertion or
- * removal of only some entries.  Similarly, Iterators and
- * Enumerations return elements reflecting the state of the hash table
- * at some point at or since the creation of the iterator/enumeration.
- * They do <em>not</em> throw {@link ConcurrentModificationException}.
- * However, iterators are designed to be used by only one thread at a time.
+ * <p>Retrieval operations (including {@code get}) generally do not
+ * block, so may overlap with update operations (including {@code put}
+ * and {@code remove}). Retrievals reflect the results of the most
+ * recently <em>completed</em> update operations holding upon their
+ * onset. (More formally, an update operation for a given key bears a
+ * <em>happens-before</em> relation with any (non-null) retrieval for
+ * that key reporting the updated value.)  For aggregate operations
+ * such as {@code putAll} and {@code clear}, concurrent retrievals may
+ * reflect insertion or removal of only some entries.  Similarly,
+ * Iterators and Enumerations return elements reflecting the state of
+ * the hash table at some point at or since the creation of the
+ * iterator/enumeration.  They do <em>not</em> throw {@link
+ * ConcurrentModificationException}.  However, iterators are designed
+ * to be used by only one thread at a time.  Bear in mind that the
+ * results of aggregate status methods including {@code size}, {@code
+ * isEmpty}, and {@code containsValue} are typically useful only when
+ * a map is not undergoing concurrent updates in other threads.
+ * Otherwise the results of these methods reflect transient states
+ * that may be adequate for monitoring or estimation purposes, but not
+ * for program control.
  *
- * <p> The allowed concurrency among update operations is guided by
- * the optional <tt>concurrencyLevel</tt> constructor argument
- * (default <tt>16</tt>), which is used as a hint for internal sizing.  The
- * table is internally partitioned to try to permit the indicated
- * number of concurrent updates without contention. Because placement
- * in hash tables is essentially random, the actual concurrency will
- * vary.  Ideally, you should choose a value to accommodate as many
- * threads as will ever concurrently modify the table. Using a
- * significantly higher value than you need can waste space and time,
- * and a significantly lower value can lead to thread contention. But
- * overestimates and underestimates within an order of magnitude do
- * not usually have much noticeable impact. A value of one is
- * appropriate when it is known that only one thread will modify and
- * all others will only read. Also, resizing this or any other kind of
- * hash table is a relatively slow operation, so, when possible, it is
- * a good idea to provide estimates of expected table sizes in
- * constructors.
+ * <p>The table is dynamically expanded when there are too many
+ * collisions (i.e., keys that have distinct hash codes but fall into
+ * the same slot modulo the table size), with the expected average
+ * effect of maintaining roughly two bins per mapping (corresponding
+ * to a 0.75 load factor threshold for resizing). There may be much
+ * variance around this average as mappings are added and removed, but
+ * overall, this maintains a commonly accepted time/space tradeoff for
+ * hash tables.  However, resizing this or any other kind of hash
+ * table may be a relatively slow operation. When possible, it is a
+ * good idea to provide a size estimate as an optional {@code
+ * initialCapacity} constructor argument. An additional optional
+ * {@code loadFactor} constructor argument provides a further means of
+ * customizing initial table capacity by specifying the table density
+ * to be used in calculating the amount of space to allocate for the
+ * given number of elements.  Also, for compatibility with previous
+ * versions of this class, constructors may optionally specify an
+ * expected {@code concurrencyLevel} as an additional hint for
+ * internal sizing.  Note that using many keys with exactly the same
+ * {@code hashCode()} is a sure way to slow down performance of any
+ * hash table. To ameliorate impact, when keys are {@link Comparable},
+ * this class may use comparison order among keys to help break ties.
  *
  * <p>This class and its views and iterators implement all of the
  * <em>optional</em> methods of the {@link Map} and {@link Iterator}
  * interfaces.
  *
- * <p> Like {@link Hashtable} but unlike {@link HashMap}, this class
- * does <em>not</em> allow <tt>null</tt> to be used as a key or value.
+ * <p>Like {@link Hashtable} but unlike {@link HashMap}, this class
+ * does <em>not</em> allow {@code null} to be used as a key or value.
  *
  * @since 1.5
  * @author Doug Lea
  * @param <K> the type of keys maintained by this map
  * @param <V> the type of mapped values
  */
-public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
-        implements ConcurrentMap<K, V>, Serializable {
+public class ConcurrentHashMap<K,V> extends java.util.AbstractMap<K,V>
+        implements ConcurrentMap<K,V>, Serializable {
     private static final long serialVersionUID = 7249069246763182397L;
 
     /*
-     * The basic strategy is to subdivide the table among Segments,
-     * each of which itself is a concurrently readable hash table.  To
-     * reduce footprint, all but one segments are constructed only
-     * when first needed (see ensureSegment). To maintain visibility
-     * in the presence of lazy construction, accesses to segments as
-     * well as elements of segment's table must use volatile access,
-     * which is done via Unsafe within methods segmentAt etc
-     * below. These provide the functionality of AtomicReferenceArrays
-     * but reduce the levels of indirection. Additionally,
-     * volatile-writes of table elements and entry "next" fields
-     * within locked operations use the cheaper "lazySet" forms of
-     * writes (via putOrderedObject) because these writes are always
-     * followed by lock releases that maintain sequential consistency
-     * of table updates.
+     * Overview:
      *
-     * Historical note: The previous version of this class relied
-     * heavily on "final" fields, which avoided some volatile reads at
-     * the expense of a large initial footprint.  Some remnants of
-     * that design (including forced construction of segment 0) exist
-     * to ensure serialization compatibility.
+     * The primary design goal of this hash table is to maintain
+     * concurrent readability (typically method get(), but also
+     * iterators and related methods) while minimizing update
+     * contention. Secondary goals are to keep space consumption about
+     * the same or better than java.util.HashMap, and to support high
+     * initial insertion rates on an empty table by many threads.
+     *
+     * This map usually acts as a binned (bucketed) hash table.  Each
+     * key-value mapping is held in a Node.  Most nodes are instances
+     * of the basic Node class with hash, key, value, and next
+     * fields. However, various subclasses exist: TreeNodes are
+     * arranged in balanced trees, not lists.  TreeBins hold the roots
+     * of sets of TreeNodes. ForwardingNodes are placed at the heads
+     * of bins during resizing. ReservationNodes are used as
+     * placeholders while establishing values in computeIfAbsent and
+     * related methods.  The types TreeBin, ForwardingNode, and
+     * ReservationNode do not hold normal user keys, values, or
+     * hashes, and are readily distinguishable during search etc
+     * because they have negative hash fields and null key and value
+     * fields. (These special nodes are either uncommon or transient,
+     * so the impact of carrying around some unused fields is
+     * insignificant.)
+     *
+     * The table is lazily initialized to a power-of-two size upon the
+     * first insertion.  Each bin in the table normally contains a
+     * list of Nodes (most often, the list has only zero or one Node).
+     * Table accesses require volatile/atomic reads, writes, and
+     * CASes.  Because there is no other way to arrange this without
+     * adding further indirections, we use intrinsics
+     * (sun.misc.Unsafe) operations.
+     *
+     * We use the top (sign) bit of Node hash fields for control
+     * purposes -- it is available anyway because of addressing
+     * constraints.  Nodes with negative hash fields are specially
+     * handled or ignored in map methods.
+     *
+     * Insertion (via put or its variants) of the first node in an
+     * empty bin is performed by just CASing it to the bin.  This is
+     * by far the most common case for put operations under most
+     * key/hash distributions.  Other update operations (insert,
+     * delete, and replace) require locks.  We do not want to waste
+     * the space required to associate a distinct lock object with
+     * each bin, so instead use the first node of a bin list itself as
+     * a lock. Locking support for these locks relies on builtin
+     * "synchronized" monitors.
+     *
+     * Using the first node of a list as a lock does not by itself
+     * suffice though: When a node is locked, any update must first
+     * validate that it is still the first node after locking it, and
+     * retry if not. Because new nodes are always appended to lists,
+     * once a node is first in a bin, it remains first until deleted
+     * or the bin becomes invalidated (upon resizing).
+     *
+     * The main disadvantage of per-bin locks is that other update
+     * operations on other nodes in a bin list protected by the same
+     * lock can stall, for example when user equals() or mapping
+     * functions take a long time.  However, statistically, under
+     * random hash codes, this is not a common problem.  Ideally, the
+     * frequency of nodes in bins follows a Poisson distribution
+     * (http://en.wikipedia.org/wiki/Poisson_distribution) with a
+     * parameter of about 0.5 on average, given the resizing threshold
+     * of 0.75, although with a large variance because of resizing
+     * granularity. Ignoring variance, the expected occurrences of
+     * list size k are (exp(-0.5) * pow(0.5, k) / factorial(k)). The
+     * first values are:
+     *
+     * 0:    0.60653066
+     * 1:    0.30326533
+     * 2:    0.07581633
+     * 3:    0.01263606
+     * 4:    0.00157952
+     * 5:    0.00015795
+     * 6:    0.00001316
+     * 7:    0.00000094
+     * 8:    0.00000006
+     * more: less than 1 in ten million
+     *
+     * Lock contention probability for two threads accessing distinct
+     * elements is roughly 1 / (8 * #elements) under random hashes.
+     *
+     * Actual hash code distributions encountered in practice
+     * sometimes deviate significantly from uniform randomness.  This
+     * includes the case when N > (1<<30), so some keys MUST collide.
+     * Similarly for dumb or hostile usages in which multiple keys are
+     * designed to have identical hash codes or ones that differs only
+     * in masked-out high bits. So we use a secondary strategy that
+     * applies when the number of nodes in a bin exceeds a
+     * threshold. These TreeBins use a balanced tree to hold nodes (a
+     * specialized form of red-black trees), bounding search time to
+     * O(log N).  Each search step in a TreeBin is at least twice as
+     * slow as in a regular list, but given that N cannot exceed
+     * (1<<64) (before running out of addresses) this bounds search
+     * steps, lock hold times, etc, to reasonable constants (roughly
+     * 100 nodes inspected per operation worst case) so long as keys
+     * are Comparable (which is very common -- String, Long, etc).
+     * TreeBin nodes (TreeNodes) also maintain the same "next"
+     * traversal pointers as regular nodes, so can be traversed in
+     * iterators in the same way.
+     *
+     * The table is resized when occupancy exceeds a percentage
+     * threshold (nominally, 0.75, but see below).  Any thread
+     * noticing an overfull bin may assist in resizing after the
+     * initiating thread allocates and sets up the replacement
+     * array. However, rather than stalling, these other threads may
+     * proceed with insertions etc.  The use of TreeBins shields us
+     * from the worst case effects of overfilling while resizes are in
+     * progress.  Resizing proceeds by transferring bins, one by one,
+     * from the table to the next table. To enable concurrency, the
+     * next table must be (incrementally) prefilled with place-holders
+     * serving as reverse forwarders to the old table.  Because we are
+     * using power-of-two expansion, the elements from each bin must
+     * either stay at same index, or move with a power of two
+     * offset. We eliminate unnecessary node creation by catching
+     * cases where old nodes can be reused because their next fields
+     * won't change.  On average, only about one-sixth of them need
+     * cloning when a table doubles. The nodes they replace will be
+     * garbage collectable as soon as they are no longer referenced by
+     * any reader thread that may be in the midst of concurrently
+     * traversing table.  Upon transfer, the old table bin contains
+     * only a special forwarding node (with hash field "MOVED") that
+     * contains the next table as its key. On encountering a
+     * forwarding node, access and update operations restart, using
+     * the new table.
+     *
+     * Each bin transfer requires its bin lock, which can stall
+     * waiting for locks while resizing. However, because other
+     * threads can join in and help resize rather than contend for
+     * locks, average aggregate waits become shorter as resizing
+     * progresses.  The transfer operation must also ensure that all
+     * accessible bins in both the old and new table are usable by any
+     * traversal.  This is arranged by proceeding from the last bin
+     * (table.length - 1) up towards the first.  Upon seeing a
+     * forwarding node, traversals (see class Traverser) arrange to
+     * move to the new table without revisiting nodes.  However, to
+     * ensure that no intervening nodes are skipped, bin splitting can
+     * only begin after the associated reverse-forwarders are in
+     * place.
+     *
+     * The traversal scheme also applies to partial traversals of
+     * ranges of bins (via an alternate Traverser constructor)
+     * to support partitioned aggregate operations.  Also, read-only
+     * operations give up if ever forwarded to a null table, which
+     * provides support for shutdown-style clearing, which is also not
+     * currently implemented.
+     *
+     * Lazy table initialization minimizes footprint until first use,
+     * and also avoids resizings when the first operation is from a
+     * putAll, constructor with map argument, or deserialization.
+     * These cases attempt to override the initial capacity settings,
+     * but harmlessly fail to take effect in cases of races.
+     *
+     * The element count is maintained using a specialization of
+     * LongAdder. We need to incorporate a specialization rather than
+     * just use a LongAdder in order to access implicit
+     * contention-sensing that leads to creation of multiple
+     * CounterCells.  The counter mechanics avoid contention on
+     * updates but can encounter cache thrashing if read too
+     * frequently during concurrent access. To avoid reading so often,
+     * resizing under contention is attempted only upon adding to a
+     * bin already holding two or more nodes. Under uniform hash
+     * distributions, the probability of this occurring at threshold
+     * is around 13%, meaning that only about 1 in 8 puts check
+     * threshold (and after resizing, many fewer do so).
+     *
+     * TreeBins use a special form of comparison for search and
+     * related operations (which is the main reason we cannot use
+     * existing collections such as TreeMaps). TreeBins contain
+     * Comparable elements, but may contain others, as well as
+     * elements that are Comparable but not necessarily Comparable
+     * for the same T, so we cannot invoke compareTo among them. To
+     * handle this, the tree is ordered primarily by hash value, then
+     * by Comparable.compareTo order if applicable.  On lookup at a
+     * node, if elements are not comparable or compare as 0 then both
+     * left and right children may need to be searched in the case of
+     * tied hash values. (This corresponds to the full list search
+     * that would be necessary if all elements were non-Comparable and
+     * had tied hashes.)  The red-black balancing code is updated from
+     * pre-jdk-collections
+     * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java)
+     * based in turn on Cormen, Leiserson, and Rivest "Introduction to
+     * Algorithms" (CLR).
+     *
+     * TreeBins also require an additional locking mechanism.  While
+     * list traversal is always possible by readers even during
+     * updates, tree traversal is not, mainly because of tree-rotations
+     * that may change the root node and/or its linkages.  TreeBins
+     * include a simple read-write lock mechanism parasitic on the
+     * main bin-synchronization strategy: Structural adjustments
+     * associated with an insertion or removal are already bin-locked
+     * (and so cannot conflict with other writers) but must wait for
+     * ongoing readers to finish. Since there can be only one such
+     * waiter, we use a simple scheme using a single "waiter" field to
+     * block writers.  However, readers need never block.  If the root
+     * lock is held, they proceed along the slow traversal path (via
+     * next-pointers) until the lock becomes available or the list is
+     * exhausted, whichever comes first. These cases are not fast, but
+     * maximize aggregate expected throughput.
+     *
+     * Maintaining API and serialization compatibility with previous
+     * versions of this class introduces several oddities. Mainly: We
+     * leave untouched but unused constructor arguments refering to
+     * concurrencyLevel. We accept a loadFactor constructor argument,
+     * but apply it only to initial table capacity (which is the only
+     * time that we can guarantee to honor it.) We also declare an
+     * unused "Segment" class that is instantiated in minimal form
+     * only when serializing.
+     *
+     * This file is organized to make things a little easier to follow
+     * while reading than they might otherwise: First the main static
+     * declarations and utilities, then fields, then main public
+     * methods (with a few factorings of multiple public methods into
+     * internal ones), then sizing methods, trees, traversers, and
+     * bulk operations.
      */
 
     /* ---------------- Constants -------------- */
 
     /**
-     * The default initial capacity for this table,
-     * used when not otherwise specified in a constructor.
+     * The largest possible table capacity.  This value must be
+     * exactly 1<<30 to stay within Java array allocation and indexing
+     * bounds for power of two table sizes, and is further required
+     * because the top two bits of 32bit hash fields are used for
+     * control purposes.
      */
-    static final int DEFAULT_INITIAL_CAPACITY = 16;
+    private static final int MAXIMUM_CAPACITY = 1 << 30;
 
     /**
-     * The default load factor for this table, used when not
-     * otherwise specified in a constructor.
+     * The default initial table capacity.  Must be a power of 2
+     * (i.e., at least 1) and at most MAXIMUM_CAPACITY.
      */
-    static final float DEFAULT_LOAD_FACTOR = 0.75f;
+    private static final int DEFAULT_CAPACITY = 16;
 
     /**
-     * The default concurrency level for this table, used when not
-     * otherwise specified in a constructor.
+     * The largest possible (non-power of two) array size.
+     * Needed by toArray and related methods.
      */
-    static final int DEFAULT_CONCURRENCY_LEVEL = 16;
+    static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
 
     /**
-     * The maximum capacity, used if a higher value is implicitly
-     * specified by either of the constructors with arguments.  MUST
-     * be a power of two <= 1<<30 to ensure that entries are indexable
-     * using ints.
+     * The default concurrency level for this table. Unused but
+     * defined for compatibility with previous versions of this class.
      */
-    static final int MAXIMUM_CAPACITY = 1 << 30;
+    private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
 
     /**
-     * The minimum capacity for per-segment tables.  Must be a power
-     * of two, at least two to avoid immediate resizing on next use
-     * after lazy construction.
+     * The load factor for this table. Overrides of this value in
+     * constructors affect only the initial table capacity.  The
+     * actual floating point value isn't normally used -- it is
+     * simpler to use expressions such as {@code n - (n >>> 2)} for
+     * the associated resizing threshold.
      */
-    static final int MIN_SEGMENT_TABLE_CAPACITY = 2;
+    private static final float LOAD_FACTOR = 0.75f;
 
     /**
-     * The maximum number of segments to allow; used to bound
-     * constructor arguments. Must be power of two less than 1 << 24.
+     * The bin count threshold for using a tree rather than list for a
+     * bin.  Bins are converted to trees when adding an element to a
+     * bin with at least this many nodes. The value must be greater
+     * than 2, and should be at least 8 to mesh with assumptions in
+     * tree removal about conversion back to plain bins upon
+     * shrinkage.
      */
-    static final int MAX_SEGMENTS = 1 << 16; // slightly conservative
+    static final int TREEIFY_THRESHOLD = 8;
 
     /**
-     * Number of unsynchronized retries in size and containsValue
-     * methods before resorting to locking. This is used to avoid
-     * unbounded retries if tables undergo continuous modification
-     * which would make it impossible to obtain an accurate result.
+     * The bin count threshold for untreeifying a (split) bin during a
+     * resize operation. Should be less than TREEIFY_THRESHOLD, and at
+     * most 6 to mesh with shrinkage detection under removal.
      */
-    static final int RETRIES_BEFORE_LOCK = 2;
+    static final int UNTREEIFY_THRESHOLD = 6;
+
+    /**
+     * The smallest table capacity for which bins may be treeified.
+     * (Otherwise the table is resized if too many nodes in a bin.)
+     * The value should be at least 4 * TREEIFY_THRESHOLD to avoid
+     * conflicts between resizing and treeification thresholds.
+     */
+    static final int MIN_TREEIFY_CAPACITY = 64;
+
+    /**
+     * Minimum number of rebinnings per transfer step. Ranges are
+     * subdivided to allow multiple resizer threads.  This value
+     * serves as a lower bound to avoid resizers encountering
+     * excessive memory contention.  The value should be at least
+     * DEFAULT_CAPACITY.
+     */
+    private static final int MIN_TRANSFER_STRIDE = 16;
+
+    /*
+     * Encodings for Node hash fields. See above for explanation.
+     */
+    static final int MOVED     = 0x8fffffff; // (-1) hash for forwarding nodes
+    static final int TREEBIN   = 0x80000000; // hash for roots of trees
+    static final int RESERVED  = 0x80000001; // hash for transient reservations
+    static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
+
+    /** Number of CPUS, to place bounds on some sizings */
+    static final int NCPU = Runtime.getRuntime().availableProcessors();
+
+    /** For serialization compatibility. */
+    private static final ObjectStreamField[] serialPersistentFields = {
+        new ObjectStreamField("segments", Segment[].class),
+        new ObjectStreamField("segmentMask", Integer.TYPE),
+        new ObjectStreamField("segmentShift", Integer.TYPE)
+    };
+
+    /* ---------------- Nodes -------------- */
+
+    /**
+     * Key-value entry.  This class is never exported out as a
+     * user-mutable Map.Entry (i.e., one supporting setValue; see
+     * MapEntry below), but can be used for read-only traversals used
+     * in bulk tasks.  Subclasses of Node with a negative hash field
+     * are special, and contain null keys and values (but are never
+     * exported).  Otherwise, keys and vals are never null.
+     */
+    static class Node<K,V> implements Map.Entry<K,V> {
+        final int hash;
+        final K key;
+        volatile V val;
+        Node<K,V> next;
+
+        Node(int hash, K key, V val, Node<K,V> next) {
+            this.hash = hash;
+            this.key = key;
+            this.val = val;
+            this.next = next;
+        }
+
+        public final K getKey()       { return key; }
+        public final V getValue()     { return val; }
+        public final int hashCode()   { return key.hashCode() ^ val.hashCode(); }
+        public final String toString(){ return key + "=" + val; }
+        public final V setValue(V value) {
+            throw new UnsupportedOperationException();
+        }
+
+        public final boolean equals(Object o) {
+            Object k, v, u; Map.Entry<?,?> e;
+            return ((o instanceof Map.Entry) &&
+                    (k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
+                    (v = e.getValue()) != null &&
+                    (k == key || k.equals(key)) &&
+                    (v == (u = val) || v.equals(u)));
+        }
+
+        /**
+         * Virtualized support for map.get(); overridden in subclasses.
+         */
+        Node<K,V> find(int h, Object k) {
+            Node<K,V> e = this;
+            if (k != null) {
+                do {
+                    K ek;
+                    if (e.hash == h &&
+                        ((ek = e.key) == k || (ek != null && k.equals(ek))))
+                        return e;
+                } while ((e = e.next) != null);
+            }
+            return null;
+        }
+    }
+
+    /* ---------------- Static utilities -------------- */
+
+    /**
+     * Spreads (XORs) higher bits of hash to lower and also forces top
+     * bit to 0. Because the table uses power-of-two masking, sets of
+     * hashes that vary only in bits above the current mask will
+     * always collide. (Among known examples are sets of Float keys
+     * holding consecutive whole numbers in small tables.)  So we
+     * apply a transform that spreads the impact of higher bits
+     * downward. There is a tradeoff between speed, utility, and
+     * quality of bit-spreading. Because many common sets of hashes
+     * are already reasonably distributed (so don't benefit from
+     * spreading), and because we use trees to handle large sets of
+     * collisions in bins, we just XOR some shifted bits in the
+     * cheapest possible way to reduce systematic lossage, as well as
+     * to incorporate impact of the highest bits that would otherwise
+     * never be used in index calculations because of table bounds.
+     */
+    static final int spread(int h) {
+        return (h ^ (h >>> 16)) & HASH_BITS;
+    }
+
+    /**
+     * Returns a power of two table size for the given desired capacity.
+     * See Hackers Delight, sec 3.2
+     */
+    private static final int tableSizeFor(int c) {
+        int n = c - 1;
+        n |= n >>> 1;
+        n |= n >>> 2;
+        n |= n >>> 4;
+        n |= n >>> 8;
+        n |= n >>> 16;
+        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+    }
+
+
+    /**
+     * Returns x's Class if it is of the form "class C implements
+     * Comparable<C>", else null.
+     */
+    static Class<?> comparableClassFor(Object x) {
+        if (x instanceof Comparable) {
+            Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
+            if ((c = x.getClass()) == String.class) // bypass checks
+                return c;
+            if ((ts = c.getGenericInterfaces()) != null) {
+                for (int i = 0; i < ts.length; ++i) {
+                    if (((t = ts[i]) instanceof ParameterizedType) &&
+                        ((p = (ParameterizedType)t).getRawType() ==
+                         Comparable.class) &&
+                        (as = p.getActualTypeArguments()) != null &&
+                        as.length == 1 && as[0] == c) // type arg is c
+                        return c;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns k.compareTo(x) if x matches kc (k's screened comparable
+     * class), else 0.
+     */
+    @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
+    static int compareComparables(Class<?> kc, Object k, Object x) {
+        return (x == null || x.getClass() != kc ? 0 :
+                ((Comparable)k).compareTo(x));
+    }
+
+    /* ---------------- Table element access -------------- */
+
+    /*
+     * Volatile access methods are used for table elements as well as
+     * elements of in-progress next table while resizing.  All uses of
+     * the tab arguments must be null checked by callers.  All callers
+     * also paranoically precheck that tab's length is not zero (or an
+     * equivalent check), thus ensuring that any index argument taking
+     * the form of a hash value anded with (length - 1) is a valid
+     * index.  Note that, to be correct wrt arbitrary concurrency
+     * errors by users, these checks must operate on local variables,
+     * which accounts for some odd-looking inline assignments below.
+     * Note that calls to setTabAt always occur within locked regions,
+     * and so do not need full volatile semantics, but still require
+     * ordering to maintain concurrent readability.
+     */
+
+    @SuppressWarnings("unchecked")
+    static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
+        return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
+    }
+
+    static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
+                                        Node<K,V> c, Node<K,V> v) {
+        return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
+    }
+
+    static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
+        U.putOrderedObject(tab, ((long)i << ASHIFT) + ABASE, v);
+    }
 
     /* ---------------- Fields -------------- */
 
     /**
-     * Mask value for indexing into segments. The upper bits of a
-     * key's hash code are used to choose the segment.
+     * The array of bins. Lazily initialized upon first insertion.
+     * Size is always a power of two. Accessed directly by iterators.
      */
-    final int segmentMask;
+    transient volatile Node<K,V>[] table;
 
     /**
-     * Shift value for indexing within segments.
+     * The next table to use; non-null only while resizing.
      */
-    final int segmentShift;
+    private transient volatile Node<K,V>[] nextTable;
 
     /**
-     * The segments, each of which is a specialized hash table.
+     * Base counter value, used mainly when there is no contention,
+     * but also as a fallback during table initialization
+     * races. Updated via CAS.
      */
-    final Segment<K,V>[] segments;
-
-    transient Set<K> keySet;
-    transient Set<Map.Entry<K,V>> entrySet;
-    transient Collection<V> values;
+    private transient volatile long baseCount;
 
     /**
-     * ConcurrentHashMap list entry. Note that this is never exported
-     * out as a user-visible Map.Entry.
+     * Table initialization and resizing control.  When negative, the
+     * table is being initialized or resized: -1 for initialization,
+     * else -(1 + the number of active resizing threads).  Otherwise,
+     * when table is null, holds the initial table size to use upon
+     * creation, or 0 for default. After initialization, holds the
+     * next element count value upon which to resize the table.
      */
-    static final class HashEntry<K,V> {
-        final int hash;
-        final K key;
-        volatile V value;
-        volatile HashEntry<K,V> next;
-
-        HashEntry(int hash, K key, V value, HashEntry<K,V> next) {
-            this.hash = hash;
-            this.key = key;
-            this.value = value;
-            this.next = next;
-        }
-
-        /**
-         * Sets next field with volatile write semantics.  (See above
-         * about use of putOrderedObject.)
-         */
-        final void setNext(HashEntry<K,V> n) {
-            UNSAFE.putOrderedObject(this, nextOffset, n);
-        }
-
-        // Unsafe mechanics
-        static final sun.misc.Unsafe UNSAFE;
-        static final long nextOffset;
-        static {
-            try {
-                UNSAFE = sun.misc.Unsafe.getUnsafe();
-                Class<?> k = HashEntry.class;
-                nextOffset = UNSAFE.objectFieldOffset
-                    (k.getDeclaredField("next"));
-            } catch (Exception e) {
-                throw new Error(e);
-            }
-        }
-    }
+    private transient volatile int sizeCtl;
 
     /**
-     * Gets the ith element of given table (if nonnull) with volatile
-     * read semantics. Note: This is manually integrated into a few
-     * performance-sensitive methods to reduce call overhead.
+     * The next table index (plus one) to split while resizing.
      */
-    @SuppressWarnings("unchecked")
-    static final <K,V> HashEntry<K,V> entryAt(HashEntry<K,V>[] tab, int i) {
-        return (tab == null) ? null :
-            (HashEntry<K,V>) UNSAFE.getObjectVolatile
-            (tab, ((long)i << TSHIFT) + TBASE);
-    }
+    private transient volatile int transferIndex;
 
     /**
-     * Sets the ith element of given table, with volatile write
-     * semantics. (See above about use of putOrderedObject.)
+     * The least available table index to split while resizing.
      */
-    static final <K,V> void setEntryAt(HashEntry<K,V>[] tab, int i,
-                                       HashEntry<K,V> e) {
-        UNSAFE.putOrderedObject(tab, ((long)i << TSHIFT) + TBASE, e);
-    }
+    private transient volatile int transferOrigin;
 
     /**
-     * Applies a supplemental hash function to a given hashCode, which
-     * defends against poor quality hash functions.  This is critical
-     * because ConcurrentHashMap uses power-of-two length hash tables,
-     * that otherwise encounter collisions for hashCodes that do not
-     * differ in lower or upper bits.
+     * Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
      */
-    private static int hash(int h) {
-        // Spread bits to regularize both segment and index locations,
-        // using variant of single-word Wang/Jenkins hash.
-        h += (h <<  15) ^ 0xffffcd7d;
-        h ^= (h >>> 10);
-        h += (h <<   3);
-        h ^= (h >>>  6);
-        h += (h <<   2) + (h << 14);
-        return h ^ (h >>> 16);
-    }
+    private transient volatile int cellsBusy;
 
     /**
-     * Segments are specialized versions of hash tables.  This
-     * subclasses from ReentrantLock opportunistically, just to
-     * simplify some locking and avoid separate construction.
+     * Table of counter cells. When non-null, size is a power of 2.
      */
-    static final class Segment<K,V> extends ReentrantLock implements Serializable {
-        /*
-         * Segments maintain a table of entry lists that are always
-         * kept in a consistent state, so can be read (via volatile
-         * reads of segments and tables) without locking.  This
-         * requires replicating nodes when necessary during table
-         * resizing, so the old lists can be traversed by readers
-         * still using old version of table.
-         *
-         * This class defines only mutative methods requiring locking.
-         * Except as noted, the methods of this class perform the
-         * per-segment versions of ConcurrentHashMap methods.  (Other
-         * methods are integrated directly into ConcurrentHashMap
-         * methods.) These mutative methods use a form of controlled
-         * spinning on contention via methods scanAndLock and
-         * scanAndLockForPut. These intersperse tryLocks with
-         * traversals to locate nodes.  The main benefit is to absorb
-         * cache misses (which are very common for hash tables) while
-         * obtaining locks so that traversal is faster once
-         * acquired. We do not actually use the found nodes since they
-         * must be re-acquired under lock anyway to ensure sequential
-         * consistency of updates (and in any case may be undetectably
-         * stale), but they will normally be much faster to re-locate.
-         * Also, scanAndLockForPut speculatively creates a fresh node
-         * to use in put if no node is found.
-         */
+    private transient volatile CounterCell[] counterCells;
 
-        private static final long serialVersionUID = 2249069246763182397L;
+    // views
+    private transient KeySetView<K,V> keySet;
+    private transient ValuesView<K,V> values;
+    private transient EntrySetView<K,V> entrySet;
 
-        /**
-         * The maximum number of times to tryLock in a prescan before
-         * possibly blocking on acquire in preparation for a locked
-         * segment operation. On multiprocessors, using a bounded
-         * number of retries maintains cache acquired while locating
-         * nodes.
-         */
-        static final int MAX_SCAN_RETRIES =
-            Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;
-
-        /**
-         * The per-segment table. Elements are accessed via
-         * entryAt/setEntryAt providing volatile semantics.
-         */
-        transient volatile HashEntry<K,V>[] table;
-
-        /**
-         * The number of elements. Accessed only either within locks
-         * or among other volatile reads that maintain visibility.
-         */
-        transient int count;
-
-        /**
-         * The total number of mutative operations in this segment.
-         * Even though this may overflows 32 bits, it provides
-         * sufficient accuracy for stability checks in CHM isEmpty()
-         * and size() methods.  Accessed only either within locks or
-         * among other volatile reads that maintain visibility.
-         */
-        transient int modCount;
-
-        /**
-         * The table is rehashed when its size exceeds this threshold.
-         * (The value of this field is always <tt>(int)(capacity *
-         * loadFactor)</tt>.)
-         */
-        transient int threshold;
-
-        /**
-         * The load factor for the hash table.  Even though this value
-         * is same for all segments, it is replicated to avoid needing
-         * links to outer object.
-         * @serial
-         */
-        final float loadFactor;
-
-        Segment(float lf, int threshold, HashEntry<K,V>[] tab) {
-            this.loadFactor = lf;
-            this.threshold = threshold;
-            this.table = tab;
-        }
-
-        final V put(K key, int hash, V value, boolean onlyIfAbsent) {
-            HashEntry<K,V> node = tryLock() ? null :
-                scanAndLockForPut(key, hash, value);
-            V oldValue;
-            try {
-                HashEntry<K,V>[] tab = table;
-                int index = (tab.length - 1) & hash;
-                HashEntry<K,V> first = entryAt(tab, index);
-                for (HashEntry<K,V> e = first;;) {
-                    if (e != null) {
-                        K k;
-                        if ((k = e.key) == key ||
-                            (e.hash == hash && key.equals(k))) {
-                            oldValue = e.value;
-                            if (!onlyIfAbsent) {
-                                e.value = value;
-                                ++modCount;
-                            }
-                            break;
-                        }
-                        e = e.next;
-                    }
-                    else {
-                        if (node != null)
-                            node.setNext(first);
-                        else
-                            node = new HashEntry<K,V>(hash, key, value, first);
-                        int c = count + 1;
-                        if (c > threshold && tab.length < MAXIMUM_CAPACITY)
-                            rehash(node);
-                        else
-                            setEntryAt(tab, index, node);
-                        ++modCount;
-                        count = c;
-                        oldValue = null;
-                        break;
-                    }
-                }
-            } finally {
-                unlock();
-            }
-            return oldValue;
-        }
-
-        /**
-         * Doubles size of table and repacks entries, also adding the
-         * given node to new table
-         */
-        @SuppressWarnings("unchecked")
-        private void rehash(HashEntry<K,V> node) {
-            /*
-             * Reclassify nodes in each list to new table.  Because we
-             * are using power-of-two expansion, the elements from
-             * each bin must either stay at same index, or move with a
-             * power of two offset. We eliminate unnecessary node
-             * creation by catching cases where old nodes can be
-             * reused because their next fields won't change.
-             * Statistically, at the default threshold, only about
-             * one-sixth of them need cloning when a table
-             * doubles. The nodes they replace will be garbage
-             * collectable as soon as they are no longer referenced by
-             * any reader thread that may be in the midst of
-             * concurrently traversing table. Entry accesses use plain
-             * array indexing because they are followed by volatile
-             * table write.
-             */
-            HashEntry<K,V>[] oldTable = table;
-            int oldCapacity = oldTable.length;
-            int newCapacity = oldCapacity << 1;
-            threshold = (int)(newCapacity * loadFactor);
-            HashEntry<K,V>[] newTable =
-                (HashEntry<K,V>[]) new HashEntry<?,?>[newCapacity];
-            int sizeMask = newCapacity - 1;
-            for (int i = 0; i < oldCapacity ; i++) {
-                HashEntry<K,V> e = oldTable[i];
-                if (e != null) {
-                    HashEntry<K,V> next = e.next;
-                    int idx = e.hash & sizeMask;
-                    if (next == null)   //  Single node on list
-                        newTable[idx] = e;
-                    else { // Reuse consecutive sequence at same slot
-                        HashEntry<K,V> lastRun = e;
-                        int lastIdx = idx;
-                        for (HashEntry<K,V> last = next;
-                             last != null;
-                             last = last.next) {
-                            int k = last.hash & sizeMask;
-                            if (k != lastIdx) {
-                                lastIdx = k;
-                                lastRun = last;
-                            }
-                        }
-                        newTable[lastIdx] = lastRun;
-                        // Clone remaining nodes
-                        for (HashEntry<K,V> p = e; p != lastRun; p = p.next) {
-                            V v = p.value;
-                            int h = p.hash;
-                            int k = h & sizeMask;
-                            HashEntry<K,V> n = newTable[k];
-                            newTable[k] = new HashEntry<K,V>(h, p.key, v, n);
-                        }
-                    }
-                }
-            }
-            int nodeIndex = node.hash & sizeMask; // add the new node
-            node.setNext(newTable[nodeIndex]);
-            newTable[nodeIndex] = node;
-            table = newTable;
-        }
-
-        /**
-         * Scans for a node containing given key while trying to
-         * acquire lock, creating and returning one if not found. Upon
-         * return, guarantees that lock is held. Unlike in most
-         * methods, calls to method equals are not screened: Since
-         * traversal speed doesn't matter, we might as well help warm
-         * up the associated code and accesses as well.
-         *
-         * @return a new node if key not found, else null
-         */
-        private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
-            HashEntry<K,V> first = entryForHash(this, hash);
-            HashEntry<K,V> e = first;
-            HashEntry<K,V> node = null;
-            int retries = -1; // negative while locating node
-            while (!tryLock()) {
-                HashEntry<K,V> f; // to recheck first below
-                if (retries < 0) {
-                    if (e == null) {
-                        if (node == null) // speculatively create node
-                            node = new HashEntry<K,V>(hash, key, value, null);
-                        retries = 0;
-                    }
-                    else if (key.equals(e.key))
-                        retries = 0;
-                    else
-                        e = e.next;
-                }
-                else if (++retries > MAX_SCAN_RETRIES) {
-                    lock();
-                    break;
-                }
-                else if ((retries & 1) == 0 &&
-                         (f = entryForHash(this, hash)) != first) {
-                    e = first = f; // re-traverse if entry changed
-                    retries = -1;
-                }
-            }
-            return node;
-        }
-
-        /**
-         * Scans for a node containing the given key while trying to
-         * acquire lock for a remove or replace operation. Upon
-         * return, guarantees that lock is held.  Note that we must
-         * lock even if the key is not found, to ensure sequential
-         * consistency of updates.
-         */
-        private void scanAndLock(Object key, int hash) {
-            // similar to but simpler than scanAndLockForPut
-            HashEntry<K,V> first = entryForHash(this, hash);
-            HashEntry<K,V> e = first;
-            int retries = -1;
-            while (!tryLock()) {
-                HashEntry<K,V> f;
-                if (retries < 0) {
-                    if (e == null || key.equals(e.key))
-                        retries = 0;
-                    else
-                        e = e.next;
-                }
-                else if (++retries > MAX_SCAN_RETRIES) {
-                    lock();
-                    break;
-                }
-                else if ((retries & 1) == 0 &&
-                         (f = entryForHash(this, hash)) != first) {
-                    e = first = f;
-                    retries = -1;
-                }
-            }
-        }
-
-        /**
-         * Remove; match on key only if value null, else match both.
-         */
-        final V remove(Object key, int hash, Object value) {
-            if (!tryLock())
-                scanAndLock(key, hash);
-            V oldValue = null;
-            try {
-                HashEntry<K,V>[] tab = table;
-                int index = (tab.length - 1) & hash;
-                HashEntry<K,V> e = entryAt(tab, index);
-                HashEntry<K,V> pred = null;
-                while (e != null) {
-                    K k;
-                    HashEntry<K,V> next = e.next;
-                    if ((k = e.key) == key ||
-                        (e.hash == hash && key.equals(k))) {
-                        V v = e.value;
-                        if (value == null || value == v || value.equals(v)) {
-                            if (pred == null)
-                                setEntryAt(tab, index, next);
-                            else
-                                pred.setNext(next);
-                            ++modCount;
-                            --count;
-                            oldValue = v;
-                        }
-                        break;
-                    }
-                    pred = e;
-                    e = next;
-                }
-            } finally {
-                unlock();
-            }
-            return oldValue;
-        }
-
-        final boolean replace(K key, int hash, V oldValue, V newValue) {
-            if (!tryLock())
-                scanAndLock(key, hash);
-            boolean replaced = false;
-            try {
-                HashEntry<K,V> e;
-                for (e = entryForHash(this, hash); e != null; e = e.next) {
-                    K k;
-                    if ((k = e.key) == key ||
-                        (e.hash == hash && key.equals(k))) {
-                        if (oldValue.equals(e.value)) {
-                            e.value = newValue;
-                            ++modCount;
-                            replaced = true;
-                        }
-                        break;
-                    }
-                }
-            } finally {
-                unlock();
-            }
-            return replaced;
-        }
-
-        final V replace(K key, int hash, V value) {
-            if (!tryLock())
-                scanAndLock(key, hash);
-            V oldValue = null;
-            try {
-                HashEntry<K,V> e;
-                for (e = entryForHash(this, hash); e != null; e = e.next) {
-                    K k;
-                    if ((k = e.key) == key ||
-                        (e.hash == hash && key.equals(k))) {
-                        oldValue = e.value;
-                        e.value = value;
-                        ++modCount;
-                        break;
-                    }
-                }
-            } finally {
-                unlock();
-            }
-            return oldValue;
-        }
-
-        final void clear() {
-            lock();
-            try {
-                HashEntry<K,V>[] tab = table;
-                for (int i = 0; i < tab.length ; i++)
-                    setEntryAt(tab, i, null);
-                ++modCount;
-                count = 0;
-            } finally {
-                unlock();
-            }
-        }
-    }
-
-    // Accessing segments
-
-    /**
-     * Gets the jth element of given segment array (if nonnull) with
-     * volatile element access semantics via Unsafe. (The null check
-     * can trigger harmlessly only during deserialization.) Note:
-     * because each element of segments array is set only once (using
-     * fully ordered writes), some performance-sensitive methods rely
-     * on this method only as a recheck upon null reads.
-     */
-    @SuppressWarnings("unchecked")
-    static final <K,V> Segment<K,V> segmentAt(Segment<K,V>[] ss, int j) {
-        long u = (j << SSHIFT) + SBASE;
-        return ss == null ? null :
-            (Segment<K,V>) UNSAFE.getObjectVolatile(ss, u);
-    }
-
-    /**
-     * Returns the segment for the given index, creating it and
-     * recording in segment table (via CAS) if not already present.
-     *
-     * @param k the index
-     * @return the segment
-     */
-    @SuppressWarnings("unchecked")
-    private Segment<K,V> ensureSegment(int k) {
-        final Segment<K,V>[] ss = this.segments;
-        long u = (k << SSHIFT) + SBASE; // raw offset
-        Segment<K,V> seg;
-        if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) {
-            Segment<K,V> proto = ss[0]; // use segment 0 as prototype
-            int cap = proto.table.length;
-            float lf = proto.loadFactor;
-            int threshold = (int)(cap * lf);
-            HashEntry<K,V>[] tab = (HashEntry<K,V>[])new HashEntry<?,?>[cap];
-            if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
-                == null) { // recheck
-                Segment<K,V> s = new Segment<K,V>(lf, threshold, tab);
-                while ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
-                       == null) {
-                    if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s))
-                        break;
-                }
-            }
-        }
-        return seg;
-    }
-
-    // Hash-based segment and entry accesses
-
-    /**
-     * Gets the segment for the given hash code.
-     */
-    @SuppressWarnings("unchecked")
-    private Segment<K,V> segmentForHash(int h) {
-        long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
-        return (Segment<K,V>) UNSAFE.getObjectVolatile(segments, u);
-    }
-
-    /**
-     * Gets the table entry for the given segment and hash code.
-     */
-    @SuppressWarnings("unchecked")
-    static final <K,V> HashEntry<K,V> entryForHash(Segment<K,V> seg, int h) {
-        HashEntry<K,V>[] tab;
-        return (seg == null || (tab = seg.table) == null) ? null :
-            (HashEntry<K,V>) UNSAFE.getObjectVolatile
-            (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
-    }
 
     /* ---------------- Public operations -------------- */
 
     /**
-     * Creates a new, empty map with the specified initial
-     * capacity, load factor and concurrency level.
-     *
-     * @param initialCapacity the initial capacity. The implementation
-     * performs internal sizing to accommodate this many elements.
-     * @param loadFactor  the load factor threshold, used to control resizing.
-     * Resizing may be performed when the average number of elements per
-     * bin exceeds this threshold.
-     * @param concurrencyLevel the estimated number of concurrently
-     * updating threads. The implementation performs internal sizing
-     * to try to accommodate this many threads.
-     * @throws IllegalArgumentException if the initial capacity is
-     * negative or the load factor or concurrencyLevel are
-     * nonpositive.
+     * Creates a new, empty map with the default initial table size (16).
      */
-    @SuppressWarnings("unchecked")
-    public ConcurrentHashMap(int initialCapacity,
-                             float loadFactor, int concurrencyLevel) {
-        if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
-            throw new IllegalArgumentException();
-        if (concurrencyLevel > MAX_SEGMENTS)
-            concurrencyLevel = MAX_SEGMENTS;
-        // Find power-of-two sizes best matching arguments
-        int sshift = 0;
-        int ssize = 1;
-        while (ssize < concurrencyLevel) {
-            ++sshift;
-            ssize <<= 1;
-        }
-        this.segmentShift = 32 - sshift;
-        this.segmentMask = ssize - 1;
-        if (initialCapacity > MAXIMUM_CAPACITY)
-            initialCapacity = MAXIMUM_CAPACITY;
-        int c = initialCapacity / ssize;
-        if (c * ssize < initialCapacity)
-            ++c;
-        int cap = MIN_SEGMENT_TABLE_CAPACITY;
-        while (cap < c)
-            cap <<= 1;
-        // create segments and segments[0]
-        Segment<K,V> s0 =
-            new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
-                             (HashEntry<K,V>[])new HashEntry<?,?>[cap]);
-        Segment<K,V>[] ss = (Segment<K,V>[])new Segment<?,?>[ssize];
-        UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
-        this.segments = ss;
+    public ConcurrentHashMap() {
     }
 
     /**
-     * Creates a new, empty map with the specified initial capacity
-     * and load factor and with the default concurrencyLevel (16).
+     * Creates a new, empty map with an initial table size
+     * accommodating the specified number of elements without the need
+     * to dynamically resize.
      *
      * @param initialCapacity The implementation performs internal
      * sizing to accommodate this many elements.
-     * @param loadFactor  the load factor threshold, used to control resizing.
-     * Resizing may be performed when the average number of elements per
-     * bin exceeds this threshold.
+     * @throws IllegalArgumentException if the initial capacity of
+     * elements is negative
+     */
+    public ConcurrentHashMap(int initialCapacity) {
+        if (initialCapacity < 0)
+            throw new IllegalArgumentException();
+        int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
+                   MAXIMUM_CAPACITY :
+                   tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
+        this.sizeCtl = cap;
+    }
+
+    /**
+     * Creates a new map with the same mappings as the given map.
+     *
+     * @param m the map
+     */
+    public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
+        this.sizeCtl = DEFAULT_CAPACITY;
+        putAll(m);
+    }
+
+    /**
+     * Creates a new, empty map with an initial table size based on
+     * the given number of elements ({@code initialCapacity}) and
+     * initial table density ({@code loadFactor}).
+     *
+     * @param initialCapacity the initial capacity. The implementation
+     * performs internal sizing to accommodate this many elements,
+     * given the specified load factor.
+     * @param loadFactor the load factor (table density) for
+     * establishing the initial table size
      * @throws IllegalArgumentException if the initial capacity of
      * elements is negative or the load factor is nonpositive
      *
      * @since 1.6
      */
     public ConcurrentHashMap(int initialCapacity, float loadFactor) {
-        this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL);
+        this(initialCapacity, loadFactor, 1);
     }
 
     /**
-     * Creates a new, empty map with the specified initial capacity,
-     * and with default load factor (0.75) and concurrencyLevel (16).
+     * Creates a new, empty map with an initial table size based on
+     * the given number of elements ({@code initialCapacity}), table
+     * density ({@code loadFactor}), and number of concurrently
+     * updating threads ({@code concurrencyLevel}).
      *
      * @param initialCapacity the initial capacity. The implementation
-     * performs internal sizing to accommodate this many elements.
-     * @throws IllegalArgumentException if the initial capacity of
-     * elements is negative.
+     * performs internal sizing to accommodate this many elements,
+     * given the specified load factor.
+     * @param loadFactor the load factor (table density) for
+     * establishing the initial table size
+     * @param concurrencyLevel the estimated number of concurrently
+     * updating threads. The implementation may use this value as
+     * a sizing hint.
+     * @throws IllegalArgumentException if the initial capacity is
+     * negative or the load factor or concurrencyLevel are
+     * nonpositive
      */
-    public ConcurrentHashMap(int initialCapacity) {
-        this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
+    public ConcurrentHashMap(int initialCapacity,
+                             float loadFactor, int concurrencyLevel) {
+        if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
+            throw new IllegalArgumentException();
+        if (initialCapacity < concurrencyLevel)   // Use at least as many bins
+            initialCapacity = concurrencyLevel;   // as estimated threads
+        long size = (long)(1.0 + (long)initialCapacity / loadFactor);
+        int cap = (size >= (long)MAXIMUM_CAPACITY) ?
+            MAXIMUM_CAPACITY : tableSizeFor((int)size);
+        this.sizeCtl = cap;
     }
 
-    /**
-     * Creates a new, empty map with a default initial capacity (16),
-     * load factor (0.75) and concurrencyLevel (16).
-     */
-    public ConcurrentHashMap() {
-        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
-    }
+    // Original (since JDK1.2) Map methods
 
     /**
-     * Creates a new map with the same mappings as the given map.
-     * The map is created with a capacity of 1.5 times the number
-     * of mappings in the given map or 16 (whichever is greater),
-     * and a default load factor (0.75) and concurrencyLevel (16).
-     *
-     * @param m the map
-     */
-    public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
-        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
-                      DEFAULT_INITIAL_CAPACITY),
-             DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
-        putAll(m);
-    }
-
-    /**
-     * Returns <tt>true</tt> if this map contains no key-value mappings.
-     *
-     * @return <tt>true</tt> if this map contains no key-value mappings
-     */
-    public boolean isEmpty() {
-        /*
-         * Sum per-segment modCounts to avoid mis-reporting when
-         * elements are concurrently added and removed in one segment
-         * while checking another, in which case the table was never
-         * actually empty at any point. (The sum ensures accuracy up
-         * through at least 1<<31 per-segment modifications before
-         * recheck.)  Methods size() and containsValue() use similar
-         * constructions for stability checks.
-         */
-        long sum = 0L;
-        final Segment<K,V>[] segments = this.segments;
-        for (int j = 0; j < segments.length; ++j) {
-            Segment<K,V> seg = segmentAt(segments, j);
-            if (seg != null) {
-                if (seg.count != 0)
-                    return false;
-                sum += seg.modCount;
-            }
-        }
-        if (sum != 0L) { // recheck unless no modifications
-            for (int j = 0; j < segments.length; ++j) {
-                Segment<K,V> seg = segmentAt(segments, j);
-                if (seg != null) {
-                    if (seg.count != 0)
-                        return false;
-                    sum -= seg.modCount;
-                }
-            }
-            if (sum != 0L)
-                return false;
-        }
-        return true;
-    }
-
-    /**
-     * Returns the number of key-value mappings in this map.  If the
-     * map contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
-     * <tt>Integer.MAX_VALUE</tt>.
-     *
-     * @return the number of key-value mappings in this map
+     * {@inheritDoc}
      */
     public int size() {
-        // Try a few times to get accurate count. On failure due to
-        // continuous async changes in table, resort to locking.
-        final Segment<K,V>[] segments = this.segments;
-        final int segmentCount = segments.length;
+        long n = sumCount();
+        return ((n < 0L) ? 0 :
+                (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
+                (int)n);
+    }
 
-        long previousSum = 0L;
-        for (int retries = -1; retries < RETRIES_BEFORE_LOCK; retries++) {
-            long sum = 0L;    // sum of modCounts
-            long size = 0L;
-            for (int i = 0; i < segmentCount; i++) {
-                Segment<K,V> segment = segmentAt(segments, i);
-                if (segment != null) {
-                    sum += segment.modCount;
-                    size += segment.count;
-                }
-            }
-            if (sum == previousSum)
-                return ((size >>> 31) == 0) ? (int) size : Integer.MAX_VALUE;
-            previousSum = sum;
-        }
-
-        long size = 0L;
-        for (int i = 0; i < segmentCount; i++) {
-            Segment<K,V> segment = ensureSegment(i);
-            segment.lock();
-            size += segment.count;
-        }
-        for (int i = 0; i < segmentCount; i++)
-            segments[i].unlock();
-        return ((size >>> 31) == 0) ? (int) size : Integer.MAX_VALUE;
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isEmpty() {
+        return sumCount() <= 0L; // ignore transient negative values
     }
 
     /**
@@ -878,18 +742,20 @@
      * @throws NullPointerException if the specified key is null
      */
     public V get(Object key) {
-        Segment<K,V> s; // manually integrate access methods to reduce overhead
-        HashEntry<K,V>[] tab;
-        int h = hash(key.hashCode());
-        long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
-        if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
-            (tab = s.table) != null) {
-            for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
-                     (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
-                 e != null; e = e.next) {
-                K k;
-                if ((k = e.key) == key || (e.hash == h && key.equals(k)))
-                    return e.value;
+        Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
+        int h = spread(key.hashCode());
+        if ((tab = table) != null && (n = tab.length) > 0 &&
+            (e = tabAt(tab, (n - 1) & h)) != null) {
+            if ((eh = e.hash) == h) {
+                if ((ek = e.key) == key || (ek != null && key.equals(ek)))
+                    return e.val;
+            }
+            else if (eh < 0)
+                return (p = e.find(h, key)) != null ? p.val : null;
+            while ((e = e.next) != null) {
+                if (e.hash == h &&
+                    ((ek = e.key) == key || (ek != null && key.equals(ek))))
+                    return e.val;
             }
         }
         return null;
@@ -898,25 +764,35 @@
     /**
      * Tests if the specified object is a key in this table.
      *
-     * @param  key   possible key
-     * @return <tt>true</tt> if and only if the specified object
+     * @param  key possible key
+     * @return {@code true} if and only if the specified object
      *         is a key in this table, as determined by the
-     *         <tt>equals</tt> method; <tt>false</tt> otherwise.
+     *         {@code equals} method; {@code false} otherwise
      * @throws NullPointerException if the specified key is null
      */
-    @SuppressWarnings("unchecked")
     public boolean containsKey(Object key) {
-        Segment<K,V> s; // same as get() except no need for volatile value read
-        HashEntry<K,V>[] tab;
-        int h = hash(key.hashCode());
-        long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
-        if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
-            (tab = s.table) != null) {
-            for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
-                     (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
-                 e != null; e = e.next) {
-                K k;
-                if ((k = e.key) == key || (e.hash == h && key.equals(k)))
+        return get(key) != null;
+    }
+
+    /**
+     * Returns {@code true} if this map maps one or more keys to the
+     * specified value. Note: This method may require a full traversal
+     * of the map, and is much slower than method {@code containsKey}.
+     *
+     * @param value value whose presence in this map is to be tested
+     * @return {@code true} if this map maps one or more keys to the
+     *         specified value
+     * @throws NullPointerException if the specified value is null
+     */
+    public boolean containsValue(Object value) {
+        if (value == null)
+            throw new NullPointerException();
+        Node<K,V>[] t;
+        if ((t = table) != null) {
+            Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+            for (Node<K,V> p; (p = it.advance()) != null; ) {
+                V v;
+                if ((v = p.val) == value || (v != null && value.equals(v)))
                     return true;
             }
         }
@@ -924,123 +800,85 @@
     }
 
     /**
-     * Returns <tt>true</tt> if this map maps one or more keys to the
-     * specified value. Note: This method requires a full internal
-     * traversal of the hash table, and so is much slower than
-     * method <tt>containsKey</tt>.
-     *
-     * @param value value whose presence in this map is to be tested
-     * @return <tt>true</tt> if this map maps one or more keys to the
-     *         specified value
-     * @throws NullPointerException if the specified value is null
-     */
-    public boolean containsValue(Object value) {
-        // Same idea as size()
-        if (value == null)
-            throw new NullPointerException();
-        final Segment<K,V>[] segments = this.segments;
-        long previousSum = 0L;
-        int lockCount = 0;
-        try {
-            for (int retries = -1; ; retries++) {
-                long sum = 0L;    // sum of modCounts
-                for (int j = 0; j < segments.length; j++) {
-                    Segment<K,V> segment;
-                    if (retries == RETRIES_BEFORE_LOCK) {
-                        segment = ensureSegment(j);
-                        segment.lock();
-                        lockCount++;
-                    } else {
-                        segment = segmentAt(segments, j);
-                        if (segment == null)
-                            continue;
-                    }
-                    HashEntry<K,V>[] tab = segment.table;
-                    if (tab != null) {
-                        for (int i = 0 ; i < tab.length; i++) {
-                            HashEntry<K,V> e;
-                            for (e = entryAt(tab, i); e != null; e = e.next) {
-                                V v = e.value;
-                                if (v != null && value.equals(v))
-                                    return true;
-                            }
-                        }
-                        sum += segment.modCount;
-                    }
-                }
-                if ((retries >= 0 && sum == previousSum) || lockCount > 0)
-                    return false;
-                previousSum = sum;
-            }
-        } finally {
-            for (int j = 0; j < lockCount; j++)
-                segments[j].unlock();
-        }
-    }
-
-    /**
-     * Legacy method testing if some key maps into the specified value
-     * in this table.  This method is identical in functionality to
-     * {@link #containsValue}, and exists solely to ensure
-     * full compatibility with class {@link java.util.Hashtable},
-     * which supported this method prior to introduction of the
-     * Java Collections framework.
-     *
-     * @param  value a value to search for
-     * @return <tt>true</tt> if and only if some key maps to the
-     *         <tt>value</tt> argument in this table as
-     *         determined by the <tt>equals</tt> method;
-     *         <tt>false</tt> otherwise
-     * @throws NullPointerException if the specified value is null
-     */
-    public boolean contains(Object value) {
-        return containsValue(value);
-    }
-
-    /**
      * Maps the specified key to the specified value in this table.
      * Neither the key nor the value can be null.
      *
-     * <p> The value can be retrieved by calling the <tt>get</tt> method
+     * <p>The value can be retrieved by calling the {@code get} method
      * with a key that is equal to the original key.
      *
      * @param key key with which the specified value is to be associated
      * @param value value to be associated with the specified key
-     * @return the previous value associated with <tt>key</tt>, or
-     *         <tt>null</tt> if there was no mapping for <tt>key</tt>
+     * @return the previous value associated with {@code key}, or
+     *         {@code null} if there was no mapping for {@code key}
      * @throws NullPointerException if the specified key or value is null
      */
-    @SuppressWarnings("unchecked")
     public V put(K key, V value) {
-        Segment<K,V> s;
-        if (value == null)
-            throw new NullPointerException();
-        int hash = hash(key.hashCode());
-        int j = (hash >>> segmentShift) & segmentMask;
-        if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
-             (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
-            s = ensureSegment(j);
-        return s.put(key, hash, value, false);
+        return putVal(key, value, false);
     }
 
-    /**
-     * {@inheritDoc}
-     *
-     * @return the previous value associated with the specified key,
-     *         or <tt>null</tt> if there was no mapping for the key
-     * @throws NullPointerException if the specified key or value is null
-     */
-    @SuppressWarnings("unchecked")
-    public V putIfAbsent(K key, V value) {
-        Segment<K,V> s;
-        if (value == null)
-            throw new NullPointerException();
-        int hash = hash(key.hashCode());
-        int j = (hash >>> segmentShift) & segmentMask;
-        if ((s = (Segment<K,V>)UNSAFE.getObject
-             (segments, (j << SSHIFT) + SBASE)) == null)
-            s = ensureSegment(j);
-        return s.put(key, hash, value, true);
+    /** Implementation for put and putIfAbsent */
+    final V putVal(K key, V value, boolean onlyIfAbsent) {
+        if (key == null || value == null) throw new NullPointerException();
+        int hash = spread(key.hashCode());
+        int binCount = 0;
+        for (Node<K,V>[] tab = table;;) {
+            Node<K,V> f; int n, i, fh;
+            if (tab == null || (n = tab.length) == 0)
+                tab = initTable();
+            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
+                if (casTabAt(tab, i, null,
+                             new Node<K,V>(hash, key, value, null)))
+                    break;                   // no lock when adding to empty bin
+            }
+            else if ((fh = f.hash) == MOVED)
+                tab = helpTransfer(tab, f);
+            else {
+                V oldVal = null;
+                synchronized (f) {
+                    if (tabAt(tab, i) == f) {
+                        if (fh >= 0) {
+                            binCount = 1;
+                            for (Node<K,V> e = f;; ++binCount) {
+                                K ek;
+                                if (e.hash == hash &&
+                                    ((ek = e.key) == key ||
+                                     (ek != null && key.equals(ek)))) {
+                                    oldVal = e.val;
+                                    if (!onlyIfAbsent)
+                                        e.val = value;
+                                    break;
+                                }
+                                Node<K,V> pred = e;
+                                if ((e = e.next) == null) {
+                                    pred.next = new Node<K,V>(hash, key,
+                                                              value, null);
+                                    break;
+                                }
+                            }
+                        }
+                        else if (f instanceof TreeBin) {
+                            Node<K,V> p;
+                            binCount = 2;
+                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
+                                                           value)) != null) {
+                                oldVal = p.val;
+                                if (!onlyIfAbsent)
+                                    p.val = value;
+                            }
+                        }
+                    }
+                }
+                if (binCount != 0) {
+                    if (binCount >= TREEIFY_THRESHOLD)
+                        treeifyBin(tab, i);
+                    if (oldVal != null)
+                        return oldVal;
+                    break;
+                }
+            }
+        }
+        addCount(1L, binCount);
+        return null;
     }
 
     /**
@@ -1051,8 +889,9 @@
      * @param m mappings to be stored in this map
      */
     public void putAll(Map<? extends K, ? extends V> m) {
+        tryPresize(m.size());
         for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
-            put(e.getKey(), e.getValue());
+            putVal(e.getKey(), e.getValue(), false);
     }
 
     /**
@@ -1060,14 +899,444 @@
      * This method does nothing if the key is not in the map.
      *
      * @param  key the key that needs to be removed
-     * @return the previous value associated with <tt>key</tt>, or
-     *         <tt>null</tt> if there was no mapping for <tt>key</tt>
+     * @return the previous value associated with {@code key}, or
+     *         {@code null} if there was no mapping for {@code key}
      * @throws NullPointerException if the specified key is null
      */
     public V remove(Object key) {
-        int hash = hash(key.hashCode());
-        Segment<K,V> s = segmentForHash(hash);
-        return s == null ? null : s.remove(key, hash, null);
+        return replaceNode(key, null, null);
+    }
+
+    /**
+     * Implementation for the four public remove/replace methods:
+     * Replaces node value with v, conditional upon match of cv if
+     * non-null.  If resulting value is null, delete.
+     */
+    final V replaceNode(Object key, V value, Object cv) {
+        int hash = spread(key.hashCode());
+        for (Node<K,V>[] tab = table;;) {
+            Node<K,V> f; int n, i, fh;
+            if (tab == null || (n = tab.length) == 0 ||
+                (f = tabAt(tab, i = (n - 1) & hash)) == null)
+                break;
+            else if ((fh = f.hash) == MOVED)
+                tab = helpTransfer(tab, f);
+            else {
+                V oldVal = null;
+                boolean validated = false;
+                synchronized (f) {
+                    if (tabAt(tab, i) == f) {
+                        if (fh >= 0) {
+                            validated = true;
+                            for (Node<K,V> e = f, pred = null;;) {
+                                K ek;
+                                if (e.hash == hash &&
+                                    ((ek = e.key) == key ||
+                                     (ek != null && key.equals(ek)))) {
+                                    V ev = e.val;
+                                    if (cv == null || cv == ev ||
+                                        (ev != null && cv.equals(ev))) {
+                                        oldVal = ev;
+                                        if (value != null)
+                                            e.val = value;
+                                        else if (pred != null)
+                                            pred.next = e.next;
+                                        else
+                                            setTabAt(tab, i, e.next);
+                                    }
+                                    break;
+                                }
+                                pred = e;
+                                if ((e = e.next) == null)
+                                    break;
+                            }
+                        }
+                        else if (f instanceof TreeBin) {
+                            validated = true;
+                            TreeBin<K,V> t = (TreeBin<K,V>)f;
+                            TreeNode<K,V> r, p;
+                            if ((r = t.root) != null &&
+                                (p = r.findTreeNode(hash, key, null)) != null) {
+                                V pv = p.val;
+                                if (cv == null || cv == pv ||
+                                    (pv != null && cv.equals(pv))) {
+                                    oldVal = pv;
+                                    if (value != null)
+                                        p.val = value;
+                                    else if (t.removeTreeNode(p))
+                                        setTabAt(tab, i, untreeify(t.first));
+                                }
+                            }
+                        }
+                    }
+                }
+                if (validated) {
+                    if (oldVal != null) {
+                        if (value == null)
+                            addCount(-1L, -1);
+                        return oldVal;
+                    }
+                    break;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Removes all of the mappings from this map.
+     */
+    public void clear() {
+        long delta = 0L; // negative number of deletions
+        int i = 0;
+        Node<K,V>[] tab = table;
+        while (tab != null && i < tab.length) {
+            int fh;
+            Node<K,V> f = tabAt(tab, i);
+            if (f == null)
+                ++i;
+            else if ((fh = f.hash) == MOVED) {
+                tab = helpTransfer(tab, f);
+                i = 0; // restart
+            }
+            else {
+                synchronized (f) {
+                    if (tabAt(tab, i) == f) {
+                        Node<K,V> p = (fh >= 0 ? f :
+                                       (f instanceof TreeBin) ?
+                                       ((TreeBin<K,V>)f).first : null);
+                        while (p != null) {
+                            --delta;
+                            p = p.next;
+                        }
+                        setTabAt(tab, i++, null);
+                    }
+                }
+            }
+        }
+        if (delta != 0L)
+            addCount(delta, -1);
+    }
+
+    /**
+     * Returns a {@link Set} view of the keys contained in this map.
+     * The set is backed by the map, so changes to the map are
+     * reflected in the set, and vice-versa. The set supports element
+     * removal, which removes the corresponding mapping from this map,
+     * via the {@code Iterator.remove}, {@code Set.remove},
+     * {@code removeAll}, {@code retainAll}, and {@code clear}
+     * operations.  It does not support the {@code add} or
+     * {@code addAll} operations.
+     *
+     * <p>The view's {@code iterator} is a "weakly consistent" iterator
+     * that will never throw {@link ConcurrentModificationException},
+     * and guarantees to traverse elements as they existed upon
+     * construction of the iterator, and may (but is not guaranteed to)
+     * reflect any modifications subsequent to construction.
+     *
+     * @return the set view
+     *
+     */
+    public Set<K> keySet() {
+        KeySetView<K,V> ks;
+        return (ks = keySet) != null ? ks : (keySet = new KeySetView<K,V>(this, null));
+    }
+
+    /**
+     * Returns a {@link Collection} view of the values contained in this map.
+     * The collection is backed by the map, so changes to the map are
+     * reflected in the collection, and vice-versa.  The collection
+     * supports element removal, which removes the corresponding
+     * mapping from this map, via the {@code Iterator.remove},
+     * {@code Collection.remove}, {@code removeAll},
+     * {@code retainAll}, and {@code clear} operations.  It does not
+     * support the {@code add} or {@code addAll} operations.
+     *
+     * <p>The view's {@code iterator} is a "weakly consistent" iterator
+     * that will never throw {@link ConcurrentModificationException},
+     * and guarantees to traverse elements as they existed upon
+     * construction of the iterator, and may (but is not guaranteed to)
+     * reflect any modifications subsequent to construction.
+     *
+     * @return the collection view
+     */
+    public Collection<V> values() {
+        ValuesView<K,V> vs;
+        return (vs = values) != null ? vs : (values = new ValuesView<K,V>(this));
+    }
+
+    /**
+     * Returns a {@link Set} view of the mappings contained in this map.
+     * The set is backed by the map, so changes to the map are
+     * reflected in the set, and vice-versa.  The set supports element
+     * removal, which removes the corresponding mapping from the map,
+     * via the {@code Iterator.remove}, {@code Set.remove},
+     * {@code removeAll}, {@code retainAll}, and {@code clear}
+     * operations.
+     *
+     * <p>The view's {@code iterator} is a "weakly consistent" iterator
+     * that will never throw {@link ConcurrentModificationException},
+     * and guarantees to traverse elements as they existed upon
+     * construction of the iterator, and may (but is not guaranteed to)
+     * reflect any modifications subsequent to construction.
+     *
+     * @return the set view
+     */
+    public Set<Map.Entry<K,V>> entrySet() {
+        EntrySetView<K,V> es;
+        return (es = entrySet) != null ? es : (entrySet = new EntrySetView<K,V>(this));
+    }
+
+    /**
+     * Returns the hash code value for this {@link Map}, i.e.,
+     * the sum of, for each key-value pair in the map,
+     * {@code key.hashCode() ^ value.hashCode()}.
+     *
+     * @return the hash code value for this map
+     */
+    public int hashCode() {
+        int h = 0;
+        Node<K,V>[] t;
+        if ((t = table) != null) {
+            Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+            for (Node<K,V> p; (p = it.advance()) != null; )
+                h += p.key.hashCode() ^ p.val.hashCode();
+        }
+        return h;
+    }
+
+    /**
+     * Returns a string representation of this map.  The string
+     * representation consists of a list of key-value mappings (in no
+     * particular order) enclosed in braces ("{@code {}}").  Adjacent
+     * mappings are separated by the characters {@code ", "} (comma
+     * and space).  Each key-value mapping is rendered as the key
+     * followed by an equals sign ("{@code =}") followed by the
+     * associated value.
+     *
+     * @return a string representation of this map
+     */
+    public String toString() {
+        Node<K,V>[] t;
+        int f = (t = table) == null ? 0 : t.length;
+        Traverser<K,V> it = new Traverser<K,V>(t, f, 0, f);
+        StringBuilder sb = new StringBuilder();
+        sb.append('{');
+        Node<K,V> p;
+        if ((p = it.advance()) != null) {
+            for (;;) {
+                K k = p.key;
+                V v = p.val;
+                sb.append(k == this ? "(this Map)" : k);
+                sb.append('=');
+                sb.append(v == this ? "(this Map)" : v);
+                if ((p = it.advance()) == null)
+                    break;
+                sb.append(',').append(' ');
+            }
+        }
+        return sb.append('}').toString();
+    }
+
+    /**
+     * Compares the specified object with this map for equality.
+     * Returns {@code true} if the given object is a map with the same
+     * mappings as this map.  This operation may return misleading
+     * results if either map is concurrently modified during execution
+     * of this method.
+     *
+     * @param o object to be compared for equality with this map
+     * @return {@code true} if the specified object is equal to this map
+     */
+    public boolean equals(Object o) {
+        if (o != this) {
+            if (!(o instanceof Map))
+                return false;
+            Map<?,?> m = (Map<?,?>) o;
+            Node<K,V>[] t;
+            int f = (t = table) == null ? 0 : t.length;
+            Traverser<K,V> it = new Traverser<K,V>(t, f, 0, f);
+            for (Node<K,V> p; (p = it.advance()) != null; ) {
+                V val = p.val;
+                Object v = m.get(p.key);
+                if (v == null || (v != val && !v.equals(val)))
+                    return false;
+            }
+            for (Map.Entry<?,?> e : m.entrySet()) {
+                Object mk, mv, v;
+                if ((mk = e.getKey()) == null ||
+                    (mv = e.getValue()) == null ||
+                    (v = get(mk)) == null ||
+                    (mv != v && !mv.equals(v)))
+                    return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Stripped-down version of helper class used in previous version,
+     * declared for the sake of serialization compatibility
+     */
+    static class Segment<K,V> extends ReentrantLock implements Serializable {
+        private static final long serialVersionUID = 2249069246763182397L;
+        final float loadFactor;
+        Segment(float lf) { this.loadFactor = lf; }
+    }
+
+    /**
+     * Saves the state of the {@code ConcurrentHashMap} instance to a
+     * stream (i.e., serializes it).
+     * @param s the stream
+     * @serialData
+     * the key (Object) and value (Object)
+     * for each key-value mapping, followed by a null pair.
+     * The key-value mappings are emitted in no particular order.
+     */
+    private void writeObject(java.io.ObjectOutputStream s)
+        throws java.io.IOException {
+        // For serialization compatibility
+        // Emulate segment calculation from previous version of this class
+        int sshift = 0;
+        int ssize = 1;
+        while (ssize < DEFAULT_CONCURRENCY_LEVEL) {
+            ++sshift;
+            ssize <<= 1;
+        }
+        int segmentShift = 32 - sshift;
+        int segmentMask = ssize - 1;
+        @SuppressWarnings("unchecked") Segment<K,V>[] segments = (Segment<K,V>[])
+            new Segment<?,?>[DEFAULT_CONCURRENCY_LEVEL];
+        for (int i = 0; i < segments.length; ++i)
+            segments[i] = new Segment<K,V>(LOAD_FACTOR);
+        s.putFields().put("segments", segments);
+        s.putFields().put("segmentShift", segmentShift);
+        s.putFields().put("segmentMask", segmentMask);
+        s.writeFields();
+
+        Node<K,V>[] t;
+        if ((t = table) != null) {
+            Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+            for (Node<K,V> p; (p = it.advance()) != null; ) {
+                s.writeObject(p.key);
+                s.writeObject(p.val);
+            }
+        }
+        s.writeObject(null);
+        s.writeObject(null);
+        segments = null; // throw away
+    }
+
+    /**
+     * Reconstitutes the instance from a stream (that is, deserializes it).
+     * @param s the stream
+     */
+    private void readObject(java.io.ObjectInputStream s)
+        throws java.io.IOException, ClassNotFoundException {
+        /*
+         * To improve performance in typical cases, we create nodes
+         * while reading, then place in table once size is known.
+         * However, we must also validate uniqueness and deal with
+         * overpopulated bins while doing so, which requires
+         * specialized versions of putVal mechanics.
+         */
+        sizeCtl = -1; // force exclusion for table construction
+        s.defaultReadObject();
+        long size = 0L;
+        Node<K,V> p = null;
+        for (;;) {
+            @SuppressWarnings("unchecked") K k = (K) s.readObject();
+            @SuppressWarnings("unchecked") V v = (V) s.readObject();
+            if (k != null && v != null) {
+                p = new Node<K,V>(spread(k.hashCode()), k, v, p);
+                ++size;
+            }
+            else
+                break;
+        }
+        if (size == 0L)
+            sizeCtl = 0;
+        else {
+            int n;
+            if (size >= (long)(MAXIMUM_CAPACITY >>> 1))
+                n = MAXIMUM_CAPACITY;
+            else {
+                int sz = (int)size;
+                n = tableSizeFor(sz + (sz >>> 1) + 1);
+            }
+            @SuppressWarnings({"rawtypes","unchecked"})
+                Node<K,V>[] tab = (Node<K,V>[])new Node[n];
+            int mask = n - 1;
+            long added = 0L;
+            while (p != null) {
+                boolean insertAtFront;
+                Node<K,V> next = p.next, first;
+                int h = p.hash, j = h & mask;
+                if ((first = tabAt(tab, j)) == null)
+                    insertAtFront = true;
+                else {
+                    K k = p.key;
+                    if (first.hash < 0) {
+                        TreeBin<K,V> t = (TreeBin<K,V>)first;
+                        if (t.putTreeVal(h, k, p.val) == null)
+                            ++added;
+                        insertAtFront = false;
+                    }
+                    else {
+                        int binCount = 0;
+                        insertAtFront = true;
+                        Node<K,V> q; K qk;
+                        for (q = first; q != null; q = q.next) {
+                            if (q.hash == h &&
+                                ((qk = q.key) == k ||
+                                 (qk != null && k.equals(qk)))) {
+                                insertAtFront = false;
+                                break;
+                            }
+                            ++binCount;
+                        }
+                        if (insertAtFront && binCount >= TREEIFY_THRESHOLD) {
+                            insertAtFront = false;
+                            ++added;
+                            p.next = first;
+                            TreeNode<K,V> hd = null, tl = null;
+                            for (q = p; q != null; q = q.next) {
+                                TreeNode<K,V> t = new TreeNode<K,V>
+                                    (q.hash, q.key, q.val, null, null);
+                                if ((t.prev = tl) == null)
+                                    hd = t;
+                                else
+                                    tl.next = t;
+                                tl = t;
+                            }
+                            setTabAt(tab, j, new TreeBin<K,V>(hd));
+                        }
+                    }
+                }
+                if (insertAtFront) {
+                    ++added;
+                    p.next = first;
+                    setTabAt(tab, j, p);
+                }
+                p = next;
+            }
+            table = tab;
+            sizeCtl = n - (n >>> 2);
+            baseCount = added;
+        }
+    }
+
+    // ConcurrentMap methods
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return the previous value associated with the specified key,
+     *         or {@code null} if there was no mapping for the key
+     * @throws NullPointerException if the specified key or value is null
+     */
+    public V putIfAbsent(K key, V value) {
+        return putVal(key, value, true);
     }
 
     /**
@@ -1076,10 +1345,9 @@
      * @throws NullPointerException if the specified key is null
      */
     public boolean remove(Object key, Object value) {
-        int hash = hash(key.hashCode());
-        Segment<K,V> s;
-        return value != null && (s = segmentForHash(hash)) != null &&
-            s.remove(key, hash, value) != null;
+        if (key == null)
+            throw new NullPointerException();
+        return value != null && replaceNode(key, null, value) != null;
     }
 
     /**
@@ -1088,101 +1356,43 @@
      * @throws NullPointerException if any of the arguments are null
      */
     public boolean replace(K key, V oldValue, V newValue) {
-        int hash = hash(key.hashCode());
-        if (oldValue == null || newValue == null)
+        if (key == null || oldValue == null || newValue == null)
             throw new NullPointerException();
-        Segment<K,V> s = segmentForHash(hash);
-        return s != null && s.replace(key, hash, oldValue, newValue);
+        return replaceNode(key, newValue, oldValue) != null;
     }
 
     /**
      * {@inheritDoc}
      *
      * @return the previous value associated with the specified key,
-     *         or <tt>null</tt> if there was no mapping for the key
+     *         or {@code null} if there was no mapping for the key
      * @throws NullPointerException if the specified key or value is null
      */
     public V replace(K key, V value) {
-        int hash = hash(key.hashCode());
-        if (value == null)
+        if (key == null || value == null)
             throw new NullPointerException();
-        Segment<K,V> s = segmentForHash(hash);
-        return s == null ? null : s.replace(key, hash, value);
+        return replaceNode(key, value, null);
     }
+    // Hashtable legacy methods
 
     /**
-     * Removes all of the mappings from this map.
-     */
-    public void clear() {
-        final Segment<K,V>[] segments = this.segments;
-        for (int j = 0; j < segments.length; ++j) {
-            Segment<K,V> s = segmentAt(segments, j);
-            if (s != null)
-                s.clear();
-        }
-    }
-
-    /**
-     * Returns a {@link Set} view of the keys contained in this map.
-     * The set is backed by the map, so changes to the map are
-     * reflected in the set, and vice-versa.  The set supports element
-     * removal, which removes the corresponding mapping from this map,
-     * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
-     * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
-     * operations.  It does not support the <tt>add</tt> or
-     * <tt>addAll</tt> operations.
+     * Legacy method testing if some key maps into the specified value
+     * in this table.  This method is identical in functionality to
+     * {@link #containsValue(Object)}, and exists solely to ensure
+     * full compatibility with class {@link java.util.Hashtable}.
      *
-     * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
-     * that will never throw {@link ConcurrentModificationException},
-     * and guarantees to traverse elements as they existed upon
-     * construction of the iterator, and may (but is not guaranteed to)
-     * reflect any modifications subsequent to construction.
+     * @param  value a value to search for
+     * @return {@code true} if and only if some key maps to the
+     *         {@code value} argument in this table as
+     *         determined by the {@code equals} method;
+     *         {@code false} otherwise
+     * @throws NullPointerException if the specified value is null
      */
-    public Set<K> keySet() {
-        Set<K> ks = keySet;
-        return (ks != null) ? ks : (keySet = new KeySet());
-    }
-
-    /**
-     * Returns a {@link Collection} view of the values contained in this map.
-     * The collection is backed by the map, so changes to the map are
-     * reflected in the collection, and vice-versa.  The collection
-     * supports element removal, which removes the corresponding
-     * mapping from this map, via the <tt>Iterator.remove</tt>,
-     * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
-     * <tt>retainAll</tt>, and <tt>clear</tt> operations.  It does not
-     * support the <tt>add</tt> or <tt>addAll</tt> operations.
-     *
-     * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
-     * that will never throw {@link ConcurrentModificationException},
-     * and guarantees to traverse elements as they existed upon
-     * construction of the iterator, and may (but is not guaranteed to)
-     * reflect any modifications subsequent to construction.
-     */
-    public Collection<V> values() {
-        Collection<V> vs = values;
-        return (vs != null) ? vs : (values = new Values());
-    }
-
-    /**
-     * Returns a {@link Set} view of the mappings contained in this map.
-     * The set is backed by the map, so changes to the map are
-     * reflected in the set, and vice-versa.  The set supports element
-     * removal, which removes the corresponding mapping from the map,
-     * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
-     * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
-     * operations.  It does not support the <tt>add</tt> or
-     * <tt>addAll</tt> operations.
-     *
-     * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
-     * that will never throw {@link ConcurrentModificationException},
-     * and guarantees to traverse elements as they existed upon
-     * construction of the iterator, and may (but is not guaranteed to)
-     * reflect any modifications subsequent to construction.
-     */
-    public Set<Map.Entry<K,V>> entrySet() {
-        Set<Map.Entry<K,V>> es = entrySet;
-        return (es != null) ? es : (entrySet = new EntrySet());
+    public boolean contains(Object value) {
+        // BEGIN android-note
+        // removed deprecation
+        // END android-note
+        return containsValue(value);
     }
 
     /**
@@ -1192,7 +1402,9 @@
      * @see #keySet()
      */
     public Enumeration<K> keys() {
-        return new KeyIterator();
+        Node<K,V>[] t;
+        int f = (t = table) == null ? 0 : t.length;
+        return new KeyIterator<K,V>(t, f, 0, f, this);
     }
 
     /**
@@ -1202,281 +1414,1787 @@
      * @see #values()
      */
     public Enumeration<V> elements() {
-        return new ValueIterator();
+        Node<K,V>[] t;
+        int f = (t = table) == null ? 0 : t.length;
+        return new ValueIterator<K,V>(t, f, 0, f, this);
     }
 
-    /* ---------------- Iterator Support -------------- */
+    // ConcurrentHashMap-only methods
 
-    abstract class HashIterator {
-        int nextSegmentIndex;
-        int nextTableIndex;
-        HashEntry<K,V>[] currentTable;
-        HashEntry<K, V> nextEntry;
-        HashEntry<K, V> lastReturned;
-
-        HashIterator() {
-            nextSegmentIndex = segments.length - 1;
-            nextTableIndex = -1;
-            advance();
-        }
-
-        /**
-         * Sets nextEntry to first node of next non-empty table
-         * (in backwards order, to simplify checks).
-         */
-        final void advance() {
-            for (;;) {
-                if (nextTableIndex >= 0) {
-                    if ((nextEntry = entryAt(currentTable,
-                                             nextTableIndex--)) != null)
-                        break;
-                }
-                else if (nextSegmentIndex >= 0) {
-                    Segment<K,V> seg = segmentAt(segments, nextSegmentIndex--);
-                    if (seg != null && (currentTable = seg.table) != null)
-                        nextTableIndex = currentTable.length - 1;
-                }
-                else
-                    break;
-            }
-        }
-
-        final HashEntry<K,V> nextEntry() {
-            HashEntry<K,V> e = nextEntry;
-            if (e == null)
-                throw new NoSuchElementException();
-            lastReturned = e; // cannot assign until after null check
-            if ((nextEntry = e.next) == null)
-                advance();
-            return e;
-        }
-
-        public final boolean hasNext() { return nextEntry != null; }
-        public final boolean hasMoreElements() { return nextEntry != null; }
-
-        public final void remove() {
-            if (lastReturned == null)
-                throw new IllegalStateException();
-            ConcurrentHashMap.this.remove(lastReturned.key);
-            lastReturned = null;
-        }
-    }
-
-    final class KeyIterator
-        extends HashIterator
-        implements Iterator<K>, Enumeration<K>
-    {
-        public final K next()        { return super.nextEntry().key; }
-        public final K nextElement() { return super.nextEntry().key; }
-    }
-
-    final class ValueIterator
-        extends HashIterator
-        implements Iterator<V>, Enumeration<V>
-    {
-        public final V next()        { return super.nextEntry().value; }
-        public final V nextElement() { return super.nextEntry().value; }
+    /**
+     * Returns the number of mappings. This method should be used
+     * instead of {@link #size} because a ConcurrentHashMap may
+     * contain more mappings than can be represented as an int. The
+     * value returned is an estimate; the actual count may differ if
+     * there are concurrent insertions or removals.
+     *
+     * @return the number of mappings
+     * @since 1.8
+     *
+     * @hide
+     */
+    public long mappingCount() {
+        long n = sumCount();
+        return (n < 0L) ? 0L : n; // ignore transient negative values
     }
 
     /**
-     * Custom Entry class used by EntryIterator.next(), that relays
-     * setValue changes to the underlying map.
+     * Creates a new {@link Set} backed by a ConcurrentHashMap
+     * from the given type to {@code Boolean.TRUE}.
+     *
+     * @return the new set
+     * @since 1.8
+     *
+     * @hide
      */
-    final class WriteThroughEntry
-        extends AbstractMap.SimpleEntry<K,V>
-    {
-        WriteThroughEntry(K k, V v) {
-            super(k,v);
+    public static <K> KeySetView<K,Boolean> newKeySet() {
+        return new KeySetView<K,Boolean>
+            (new ConcurrentHashMap<K,Boolean>(), Boolean.TRUE);
+    }
+
+    /**
+     * Creates a new {@link Set} backed by a ConcurrentHashMap
+     * from the given type to {@code Boolean.TRUE}.
+     *
+     * @param initialCapacity The implementation performs internal
+     * sizing to accommodate this many elements.
+     * @throws IllegalArgumentException if the initial capacity of
+     * elements is negative
+     * @return the new set
+     * @since 1.8
+     *
+     * @hide
+     */
+    public static <K> KeySetView<K,Boolean> newKeySet(int initialCapacity) {
+        return new KeySetView<K,Boolean>
+            (new ConcurrentHashMap<K,Boolean>(initialCapacity), Boolean.TRUE);
+    }
+
+    /**
+     * Returns a {@link Set} view of the keys in this map, using the
+     * given common mapped value for any additions (i.e., {@link
+     * Collection#add} and {@link Collection#addAll(Collection)}).
+     * This is of course only appropriate if it is acceptable to use
+     * the same value for all additions from this view.
+     *
+     * @param mappedValue the mapped value to use for any additions
+     * @return the set view
+     * @throws NullPointerException if the mappedValue is null
+     *
+     * @hide
+     */
+    public Set<K> keySet(V mappedValue) {
+        if (mappedValue == null)
+            throw new NullPointerException();
+        return new KeySetView<K,V>(this, mappedValue);
+    }
+
+    /* ---------------- Special Nodes -------------- */
+
+    /**
+     * A node inserted at head of bins during transfer operations.
+     */
+    static final class ForwardingNode<K,V> extends Node<K,V> {
+        final Node<K,V>[] nextTable;
+        ForwardingNode(Node<K,V>[] tab) {
+            super(MOVED, null, null, null);
+            this.nextTable = tab;
+        }
+
+        Node<K,V> find(int h, Object k) {
+            Node<K,V> e; int n;
+            Node<K,V>[] tab = nextTable;
+            if (k != null && tab != null && (n = tab.length) > 0 &&
+                (e = tabAt(tab, (n - 1) & h)) != null) {
+                do {
+                    int eh; K ek;
+                    if ((eh = e.hash) == h &&
+                        ((ek = e.key) == k || (ek != null && k.equals(ek))))
+                        return e;
+                    if (eh < 0)
+                        return e.find(h, k);
+                } while ((e = e.next) != null);
+            }
+            return null;
+        }
+    }
+
+    /**
+     * A place-holder node used in computeIfAbsent and compute
+     */
+    static final class ReservationNode<K,V> extends Node<K,V> {
+        ReservationNode() {
+            super(RESERVED, null, null, null);
+        }
+
+        Node<K,V> find(int h, Object k) {
+            return null;
+        }
+    }
+
+    /* ---------------- Table Initialization and Resizing -------------- */
+
+    /**
+     * Initializes table, using the size recorded in sizeCtl.
+     */
+    private final Node<K,V>[] initTable() {
+        Node<K,V>[] tab; int sc;
+        while ((tab = table) == null || tab.length == 0) {
+            if ((sc = sizeCtl) < 0)
+                Thread.yield(); // lost initialization race; just spin
+            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
+                try {
+                    if ((tab = table) == null || tab.length == 0) {
+                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
+                        @SuppressWarnings({"rawtypes","unchecked"})
+                            Node<K,V>[] nt = (Node<K,V>[])new Node[n];
+                        table = tab = nt;
+                        sc = n - (n >>> 2);
+                    }
+                } finally {
+                    sizeCtl = sc;
+                }
+                break;
+            }
+        }
+        return tab;
+    }
+
+    /**
+     * Adds to count, and if table is too small and not already
+     * resizing, initiates transfer. If already resizing, helps
+     * perform transfer if work is available.  Rechecks occupancy
+     * after a transfer to see if another resize is already needed
+     * because resizings are lagging additions.
+     *
+     * @param x the count to add
+     * @param check if <0, don't check resize, if <= 1 only check if uncontended
+     */
+    private final void addCount(long x, int check) {
+        CounterCell[] as; long b, s;
+        if ((as = counterCells) != null ||
+            !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
+            CounterHashCode hc; CounterCell a; long v; int m;
+            boolean uncontended = true;
+            if ((hc = threadCounterHashCode.get()) == null ||
+                as == null || (m = as.length - 1) < 0 ||
+                (a = as[m & hc.code]) == null ||
+                !(uncontended =
+                  U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
+                fullAddCount(x, hc, uncontended);
+                return;
+            }
+            if (check <= 1)
+                return;
+            s = sumCount();
+        }
+        if (check >= 0) {
+            Node<K,V>[] tab, nt; int sc;
+            while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
+                   tab.length < MAXIMUM_CAPACITY) {
+                if (sc < 0) {
+                    if (sc == -1 || transferIndex <= transferOrigin ||
+                        (nt = nextTable) == null)
+                        break;
+                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc - 1))
+                        transfer(tab, nt);
+                }
+                else if (U.compareAndSwapInt(this, SIZECTL, sc, -2))
+                    transfer(tab, null);
+                s = sumCount();
+            }
+        }
+    }
+
+    /**
+     * Helps transfer if a resize is in progress.
+     */
+    final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
+        Node<K,V>[] nextTab; int sc;
+        if ((f instanceof ForwardingNode) &&
+            (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
+            if (nextTab == nextTable && tab == table &&
+                transferIndex > transferOrigin && (sc = sizeCtl) < -1 &&
+                U.compareAndSwapInt(this, SIZECTL, sc, sc - 1))
+                transfer(tab, nextTab);
+            return nextTab;
+        }
+        return table;
+    }
+
+    /**
+     * Tries to presize table to accommodate the given number of elements.
+     *
+     * @param size number of elements (doesn't need to be perfectly accurate)
+     */
+    private final void tryPresize(int size) {
+        int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY :
+            tableSizeFor(size + (size >>> 1) + 1);
+        int sc;
+        while ((sc = sizeCtl) >= 0) {
+            Node<K,V>[] tab = table; int n;
+            if (tab == null || (n = tab.length) == 0) {
+                n = (sc > c) ? sc : c;
+                if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
+                    try {
+                        if (table == tab) {
+                            @SuppressWarnings({"rawtypes","unchecked"})
+                                Node<K,V>[] nt = (Node<K,V>[])new Node[n];
+                            table = nt;
+                            sc = n - (n >>> 2);
+                        }
+                    } finally {
+                        sizeCtl = sc;
+                    }
+                }
+            }
+            else if (c <= sc || n >= MAXIMUM_CAPACITY)
+                break;
+            else if (tab == table &&
+                     U.compareAndSwapInt(this, SIZECTL, sc, -2))
+                transfer(tab, null);
+        }
+    }
+
+    /**
+     * Moves and/or copies the nodes in each bin to new table. See
+     * above for explanation.
+     */
+    private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
+        int n = tab.length, stride;
+        if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
+            stride = MIN_TRANSFER_STRIDE; // subdivide range
+        if (nextTab == null) {            // initiating
+            try {
+                @SuppressWarnings({"rawtypes","unchecked"})
+                    Node<K,V>[] nt = (Node<K,V>[])new Node[n << 1];
+                nextTab = nt;
+            } catch (Throwable ex) {      // try to cope with OOME
+                sizeCtl = Integer.MAX_VALUE;
+                return;
+            }
+            nextTable = nextTab;
+            transferOrigin = n;
+            transferIndex = n;
+            ForwardingNode<K,V> rev = new ForwardingNode<K,V>(tab);
+            for (int k = n; k > 0;) {    // progressively reveal ready slots
+                int nextk = (k > stride) ? k - stride : 0;
+                for (int m = nextk; m < k; ++m)
+                    nextTab[m] = rev;
+                for (int m = n + nextk; m < n + k; ++m)
+                    nextTab[m] = rev;
+                U.putOrderedInt(this, TRANSFERORIGIN, k = nextk);
+            }
+        }
+        int nextn = nextTab.length;
+        ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
+        boolean advance = true;
+        for (int i = 0, bound = 0;;) {
+            int nextIndex, nextBound, fh; Node<K,V> f;
+            while (advance) {
+                if (--i >= bound)
+                    advance = false;
+                else if ((nextIndex = transferIndex) <= transferOrigin) {
+                    i = -1;
+                    advance = false;
+                }
+                else if (U.compareAndSwapInt
+                         (this, TRANSFERINDEX, nextIndex,
+                          nextBound = (nextIndex > stride ?
+                                       nextIndex - stride : 0))) {
+                    bound = nextBound;
+                    i = nextIndex - 1;
+                    advance = false;
+                }
+            }
+            if (i < 0 || i >= n || i + n >= nextn) {
+                for (int sc;;) {
+                    if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, ++sc)) {
+                        if (sc == -1) {
+                            nextTable = null;
+                            table = nextTab;
+                            sizeCtl = (n << 1) - (n >>> 1);
+                        }
+                        return;
+                    }
+                }
+            }
+            else if ((f = tabAt(tab, i)) == null) {
+                if (casTabAt(tab, i, null, fwd)) {
+                    setTabAt(nextTab, i, null);
+                    setTabAt(nextTab, i + n, null);
+                    advance = true;
+                }
+            }
+            else if ((fh = f.hash) == MOVED)
+                advance = true; // already processed
+            else {
+                synchronized (f) {
+                    if (tabAt(tab, i) == f) {
+                        Node<K,V> ln, hn;
+                        if (fh >= 0) {
+                            int runBit = fh & n;
+                            Node<K,V> lastRun = f;
+                            for (Node<K,V> p = f.next; p != null; p = p.next) {
+                                int b = p.hash & n;
+                                if (b != runBit) {
+                                    runBit = b;
+                                    lastRun = p;
+                                }
+                            }
+                            if (runBit == 0) {
+                                ln = lastRun;
+                                hn = null;
+                            }
+                            else {
+                                hn = lastRun;
+                                ln = null;
+                            }
+                            for (Node<K,V> p = f; p != lastRun; p = p.next) {
+                                int ph = p.hash; K pk = p.key; V pv = p.val;
+                                if ((ph & n) == 0)
+                                    ln = new Node<K,V>(ph, pk, pv, ln);
+                                else
+                                    hn = new Node<K,V>(ph, pk, pv, hn);
+                            }
+                        }
+                        else if (f instanceof TreeBin) {
+                            TreeBin<K,V> t = (TreeBin<K,V>)f;
+                            TreeNode<K,V> lo = null, loTail = null;
+                            TreeNode<K,V> hi = null, hiTail = null;
+                            int lc = 0, hc = 0;
+                            for (Node<K,V> e = t.first; e != null; e = e.next) {
+                                int h = e.hash;
+                                TreeNode<K,V> p = new TreeNode<K,V>
+                                    (h, e.key, e.val, null, null);
+                                if ((h & n) == 0) {
+                                    if ((p.prev = loTail) == null)
+                                        lo = p;
+                                    else
+                                        loTail.next = p;
+                                    loTail = p;
+                                    ++lc;
+                                }
+                                else {
+                                    if ((p.prev = hiTail) == null)
+                                        hi = p;
+                                    else
+                                        hiTail.next = p;
+                                    hiTail = p;
+                                    ++hc;
+                                }
+                            }
+                            ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
+                                (hc != 0) ? new TreeBin<K,V>(lo) : t;
+                            hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
+                                (lc != 0) ? new TreeBin<K,V>(hi) : t;
+                        }
+                        else
+                            ln = hn = null;
+                        setTabAt(nextTab, i, ln);
+                        setTabAt(nextTab, i + n, hn);
+                        setTabAt(tab, i, fwd);
+                        advance = true;
+                    }
+                }
+            }
+        }
+    }
+
+    /* ---------------- Conversion from/to TreeBins -------------- */
+
+    /**
+     * Replaces all linked nodes in bin at given index unless table is
+     * too small, in which case resizes instead.
+     */
+    private final void treeifyBin(Node<K,V>[] tab, int index) {
+        Node<K,V> b; int n, sc;
+        if (tab != null) {
+            if ((n = tab.length) < MIN_TREEIFY_CAPACITY) {
+                if (tab == table && (sc = sizeCtl) >= 0 &&
+                    U.compareAndSwapInt(this, SIZECTL, sc, -2))
+                    transfer(tab, null);
+            }
+            else if ((b = tabAt(tab, index)) != null) {
+                synchronized (b) {
+                    if (tabAt(tab, index) == b) {
+                        TreeNode<K,V> hd = null, tl = null;
+                        for (Node<K,V> e = b; e != null; e = e.next) {
+                            TreeNode<K,V> p =
+                                new TreeNode<K,V>(e.hash, e.key, e.val,
+                                                  null, null);
+                            if ((p.prev = tl) == null)
+                                hd = p;
+                            else
+                                tl.next = p;
+                            tl = p;
+                        }
+                        setTabAt(tab, index, new TreeBin<K,V>(hd));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns a list on non-TreeNodes replacing those in given list.
+     */
+    static <K,V> Node<K,V> untreeify(Node<K,V> b) {
+        Node<K,V> hd = null, tl = null;
+        for (Node<K,V> q = b; q != null; q = q.next) {
+            Node<K,V> p = new Node<K,V>(q.hash, q.key, q.val, null);
+            if (tl == null)
+                hd = p;
+            else
+                tl.next = p;
+            tl = p;
+        }
+        return hd;
+    }
+
+    /* ---------------- TreeNodes -------------- */
+
+    /**
+     * Nodes for use in TreeBins
+     */
+    static final class TreeNode<K,V> extends Node<K,V> {
+        TreeNode<K,V> parent;  // red-black tree links
+        TreeNode<K,V> left;
+        TreeNode<K,V> right;
+        TreeNode<K,V> prev;    // needed to unlink next upon deletion
+        boolean red;
+
+        TreeNode(int hash, K key, V val, Node<K,V> next,
+                 TreeNode<K,V> parent) {
+            super(hash, key, val, next);
+            this.parent = parent;
+        }
+
+        Node<K,V> find(int h, Object k) {
+            return findTreeNode(h, k, null);
+        }
+
+        /**
+         * Returns the TreeNode (or null if not found) for the given key
+         * starting at given root.
+         */
+        final TreeNode<K,V> findTreeNode(int h, Object k, Class<?> kc) {
+            if (k != null) {
+                TreeNode<K,V> p = this;
+                do  {
+                    int ph, dir; K pk; TreeNode<K,V> q;
+                    TreeNode<K,V> pl = p.left, pr = p.right;
+                    if ((ph = p.hash) > h)
+                        p = pl;
+                    else if (ph < h)
+                        p = pr;
+                    else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
+                        return p;
+                    else if (pl == null && pr == null)
+                        break;
+                    else if ((kc != null ||
+                              (kc = comparableClassFor(k)) != null) &&
+                             (dir = compareComparables(kc, k, pk)) != 0)
+                        p = (dir < 0) ? pl : pr;
+                    else if (pl == null)
+                        p = pr;
+                    else if (pr == null ||
+                             (q = pr.findTreeNode(h, k, kc)) == null)
+                        p = pl;
+                    else
+                        return q;
+                } while (p != null);
+            }
+            return null;
+        }
+    }
+
+    /* ---------------- TreeBins -------------- */
+
+    /**
+     * TreeNodes used at the heads of bins. TreeBins do not hold user
+     * keys or values, but instead point to list of TreeNodes and
+     * their root. They also maintain a parasitic read-write lock
+     * forcing writers (who hold bin lock) to wait for readers (who do
+     * not) to complete before tree restructuring operations.
+     */
+    static final class TreeBin<K,V> extends Node<K,V> {
+        TreeNode<K,V> root;
+        volatile TreeNode<K,V> first;
+        volatile Thread waiter;
+        volatile int lockState;
+        // values for lockState
+        static final int WRITER = 1; // set while holding write lock
+        static final int WAITER = 2; // set when waiting for write lock
+        static final int READER = 4; // increment value for setting read lock
+
+        /**
+         * Creates bin with initial set of nodes headed by b.
+         */
+        TreeBin(TreeNode<K,V> b) {
+            super(TREEBIN, null, null, null);
+            this.first = b;
+            TreeNode<K,V> r = null;
+            for (TreeNode<K,V> x = b, next; x != null; x = next) {
+                next = (TreeNode<K,V>)x.next;
+                x.left = x.right = null;
+                if (r == null) {
+                    x.parent = null;
+                    x.red = false;
+                    r = x;
+                }
+                else {
+                    Object key = x.key;
+                    int hash = x.hash;
+                    Class<?> kc = null;
+                    for (TreeNode<K,V> p = r;;) {
+                        int dir, ph;
+                        if ((ph = p.hash) > hash)
+                            dir = -1;
+                        else if (ph < hash)
+                            dir = 1;
+                        else if ((kc != null ||
+                                  (kc = comparableClassFor(key)) != null))
+                            dir = compareComparables(kc, key, p.key);
+                        else
+                            dir = 0;
+                        TreeNode<K,V> xp = p;
+                        if ((p = (dir <= 0) ? p.left : p.right) == null) {
+                            x.parent = xp;
+                            if (dir <= 0)
+                                xp.left = x;
+                            else
+                                xp.right = x;
+                            r = balanceInsertion(r, x);
+                            break;
+                        }
+                    }
+                }
+            }
+            this.root = r;
+        }
+
+        /**
+         * Acquires write lock for tree restructuring.
+         */
+        private final void lockRoot() {
+            if (!U.compareAndSwapInt(this, LOCKSTATE, 0, WRITER))
+                contendedLock(); // offload to separate method
+        }
+
+        /**
+         * Releases write lock for tree restructuring.
+         */
+        private final void unlockRoot() {
+            lockState = 0;
+        }
+
+        /**
+         * Possibly blocks awaiting root lock.
+         */
+        private final void contendedLock() {
+            boolean waiting = false;
+            for (int s;;) {
+                if (((s = lockState) & WRITER) == 0) {
+                    if (U.compareAndSwapInt(this, LOCKSTATE, s, WRITER)) {
+                        if (waiting)
+                            waiter = null;
+                        return;
+                    }
+                }
+                else if ((s | WAITER) == 0) {
+                    if (U.compareAndSwapInt(this, LOCKSTATE, s, s | WAITER)) {
+                        waiting = true;
+                        waiter = Thread.currentThread();
+                    }
+                }
+                else if (waiting)
+                    LockSupport.park(this);
+            }
+        }
+
+        /**
+         * Returns matching node or null if none. Tries to search
+         * using tree comparisons from root, but continues linear
+         * search when lock not available.
+         */
+        final Node<K,V> find(int h, Object k) {
+            if (k != null) {
+                for (Node<K,V> e = first; e != null; e = e.next) {
+                    int s; K ek;
+                    if (((s = lockState) & (WAITER|WRITER)) != 0) {
+                        if (e.hash == h &&
+                            ((ek = e.key) == k || (ek != null && k.equals(ek))))
+                            return e;
+                    }
+                    else if (U.compareAndSwapInt(this, LOCKSTATE, s,
+                                                 s + READER)) {
+                        TreeNode<K,V> r, p;
+                        try {
+                            p = ((r = root) == null ? null :
+                                 r.findTreeNode(h, k, null));
+                        } finally {
+
+                            Thread w;
+                            int ls;
+                            do {} while (!U.compareAndSwapInt
+                                         (this, LOCKSTATE,
+                                          ls = lockState, ls - READER));
+                            if (ls == (READER|WAITER) && (w = waiter) != null)
+                                LockSupport.unpark(w);
+                        }
+                        return p;
+                    }
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Finds or adds a node.
+         * @return null if added
+         */
+        final TreeNode<K,V> putTreeVal(int h, K k, V v) {
+            Class<?> kc = null;
+            for (TreeNode<K,V> p = root;;) {
+                int dir, ph; K pk; TreeNode<K,V> q, pr;
+                if (p == null) {
+                    first = root = new TreeNode<K,V>(h, k, v, null, null);
+                    break;
+                }
+                else if ((ph = p.hash) > h)
+                    dir = -1;
+                else if (ph < h)
+                    dir = 1;
+                else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
+                    return p;
+                else if ((kc == null &&
+                          (kc = comparableClassFor(k)) == null) ||
+                         (dir = compareComparables(kc, k, pk)) == 0) {
+                    if (p.left == null)
+                        dir = 1;
+                    else if ((pr = p.right) == null ||
+                             (q = pr.findTreeNode(h, k, kc)) == null)
+                        dir = -1;
+                    else
+                        return q;
+                }
+                TreeNode<K,V> xp = p;
+                if ((p = (dir < 0) ? p.left : p.right) == null) {
+                    TreeNode<K,V> x, f = first;
+                    first = x = new TreeNode<K,V>(h, k, v, f, xp);
+                    if (f != null)
+                        f.prev = x;
+                    if (dir < 0)
+                        xp.left = x;
+                    else
+                        xp.right = x;
+                    if (!xp.red)
+                        x.red = true;
+                    else {
+                        lockRoot();
+                        try {
+                            root = balanceInsertion(root, x);
+                        } finally {
+                            unlockRoot();
+                        }
+                    }
+                    break;
+                }
+            }
+            assert checkInvariants(root);
+            return null;
+        }
+
+        /**
+         * Removes the given node, that must be present before this
+         * call.  This is messier than typical red-black deletion code
+         * because we cannot swap the contents of an interior node
+         * with a leaf successor that is pinned by "next" pointers
+         * that are accessible independently of lock. So instead we
+         * swap the tree linkages.
+         *
+         * @return true if now too small, so should be untreeified
+         */
+        final boolean removeTreeNode(TreeNode<K,V> p) {
+            TreeNode<K,V> next = (TreeNode<K,V>)p.next;
+            TreeNode<K,V> pred = p.prev;  // unlink traversal pointers
+            TreeNode<K,V> r, rl;
+            if (pred == null)
+                first = next;
+            else
+                pred.next = next;
+            if (next != null)
+                next.prev = pred;
+            if (first == null) {
+                root = null;
+                return true;
+            }
+            if ((r = root) == null || r.right == null || // too small
+                (rl = r.left) == null || rl.left == null)
+                return true;
+            lockRoot();
+            try {
+                TreeNode<K,V> replacement;
+                TreeNode<K,V> pl = p.left;
+                TreeNode<K,V> pr = p.right;
+                if (pl != null && pr != null) {
+                    TreeNode<K,V> s = pr, sl;
+                    while ((sl = s.left) != null) // find successor
+                        s = sl;
+                    boolean c = s.red; s.red = p.red; p.red = c; // swap colors
+                    TreeNode<K,V> sr = s.right;
+                    TreeNode<K,V> pp = p.parent;
+                    if (s == pr) { // p was s's direct parent
+                        p.parent = s;
+                        s.right = p;
+                    }
+                    else {
+                        TreeNode<K,V> sp = s.parent;
+                        if ((p.parent = sp) != null) {
+                            if (s == sp.left)
+                                sp.left = p;
+                            else
+                                sp.right = p;
+                        }
+                        if ((s.right = pr) != null)
+                            pr.parent = s;
+                    }
+                    p.left = null;
+                    if ((p.right = sr) != null)
+                        sr.parent = p;
+                    if ((s.left = pl) != null)
+                        pl.parent = s;
+                    if ((s.parent = pp) == null)
+                        r = s;
+                    else if (p == pp.left)
+                        pp.left = s;
+                    else
+                        pp.right = s;
+                    if (sr != null)
+                        replacement = sr;
+                    else
+                        replacement = p;
+                }
+                else if (pl != null)
+                    replacement = pl;
+                else if (pr != null)
+                    replacement = pr;
+                else
+                    replacement = p;
+                if (replacement != p) {
+                    TreeNode<K,V> pp = replacement.parent = p.parent;
+                    if (pp == null)
+                        r = replacement;
+                    else if (p == pp.left)
+                        pp.left = replacement;
+                    else
+                        pp.right = replacement;
+                    p.left = p.right = p.parent = null;
+                }
+
+                root = (p.red) ? r : balanceDeletion(r, replacement);
+
+                if (p == replacement) {  // detach pointers
+                    TreeNode<K,V> pp;
+                    if ((pp = p.parent) != null) {
+                        if (p == pp.left)
+                            pp.left = null;
+                        else if (p == pp.right)
+                            pp.right = null;
+                        p.parent = null;
+                    }
+                }
+            } finally {
+                unlockRoot();
+            }
+            assert checkInvariants(root);
+            return false;
+        }
+
+        /* ------------------------------------------------------------ */
+        // Red-black tree methods, all adapted from CLR
+
+        static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
+                                              TreeNode<K,V> p) {
+            TreeNode<K,V> r, pp, rl;
+            if (p != null && (r = p.right) != null) {
+                if ((rl = p.right = r.left) != null)
+                    rl.parent = p;
+                if ((pp = r.parent = p.parent) == null)
+                    (root = r).red = false;
+                else if (pp.left == p)
+                    pp.left = r;
+                else
+                    pp.right = r;
+                r.left = p;
+                p.parent = r;
+            }
+            return root;
+        }
+
+        static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
+                                               TreeNode<K,V> p) {
+            TreeNode<K,V> l, pp, lr;
+            if (p != null && (l = p.left) != null) {
+                if ((lr = p.left = l.right) != null)
+                    lr.parent = p;
+                if ((pp = l.parent = p.parent) == null)
+                    (root = l).red = false;
+                else if (pp.right == p)
+                    pp.right = l;
+                else
+                    pp.left = l;
+                l.right = p;
+                p.parent = l;
+            }
+            return root;
+        }
+
+        static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
+                                                    TreeNode<K,V> x) {
+            x.red = true;
+            for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
+                if ((xp = x.parent) == null) {
+                    x.red = false;
+                    return x;
+                }
+                else if (!xp.red || (xpp = xp.parent) == null)
+                    return root;
+                if (xp == (xppl = xpp.left)) {
+                    if ((xppr = xpp.right) != null && xppr.red) {
+                        xppr.red = false;
+                        xp.red = false;
+                        xpp.red = true;
+                        x = xpp;
+                    }
+                    else {
+                        if (x == xp.right) {
+                            root = rotateLeft(root, x = xp);
+                            xpp = (xp = x.parent) == null ? null : xp.parent;
+                        }
+                        if (xp != null) {
+                            xp.red = false;
+                            if (xpp != null) {
+                                xpp.red = true;
+                                root = rotateRight(root, xpp);
+                            }
+                        }
+                    }
+                }
+                else {
+                    if (xppl != null && xppl.red) {
+                        xppl.red = false;
+                        xp.red = false;
+                        xpp.red = true;
+                        x = xpp;
+                    }
+                    else {
+                        if (x == xp.left) {
+                            root = rotateRight(root, x = xp);
+                            xpp = (xp = x.parent) == null ? null : xp.parent;
+                        }
+                        if (xp != null) {
+                            xp.red = false;
+                            if (xpp != null) {
+                                xpp.red = true;
+                                root = rotateLeft(root, xpp);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
+                                                   TreeNode<K,V> x) {
+            for (TreeNode<K,V> xp, xpl, xpr;;)  {
+                if (x == null || x == root)
+                    return root;
+                else if ((xp = x.parent) == null) {
+                    x.red = false;
+                    return x;
+                }
+                else if (x.red) {
+                    x.red = false;
+                    return root;
+                }
+                else if ((xpl = xp.left) == x) {
+                    if ((xpr = xp.right) != null && xpr.red) {
+                        xpr.red = false;
+                        xp.red = true;
+                        root = rotateLeft(root, xp);
+                        xpr = (xp = x.parent) == null ? null : xp.right;
+                    }
+                    if (xpr == null)
+                        x = xp;
+                    else {
+                        TreeNode<K,V> sl = xpr.left, sr = xpr.right;
+                        if ((sr == null || !sr.red) &&
+                            (sl == null || !sl.red)) {
+                            xpr.red = true;
+                            x = xp;
+                        }
+                        else {
+                            if (sr == null || !sr.red) {
+                                if (sl != null)
+                                    sl.red = false;
+                                xpr.red = true;
+                                root = rotateRight(root, xpr);
+                                xpr = (xp = x.parent) == null ?
+                                    null : xp.right;
+                            }
+                            if (xpr != null) {
+                                xpr.red = (xp == null) ? false : xp.red;
+                                if ((sr = xpr.right) != null)
+                                    sr.red = false;
+                            }
+                            if (xp != null) {
+                                xp.red = false;
+                                root = rotateLeft(root, xp);
+                            }
+                            x = root;
+                        }
+                    }
+                }
+                else { // symmetric
+                    if (xpl != null && xpl.red) {
+                        xpl.red = false;
+                        xp.red = true;
+                        root = rotateRight(root, xp);
+                        xpl = (xp = x.parent) == null ? null : xp.left;
+                    }
+                    if (xpl == null)
+                        x = xp;
+                    else {
+                        TreeNode<K,V> sl = xpl.left, sr = xpl.right;
+                        if ((sl == null || !sl.red) &&
+                            (sr == null || !sr.red)) {
+                            xpl.red = true;
+                            x = xp;
+                        }
+                        else {
+                            if (sl == null || !sl.red) {
+                                if (sr != null)
+                                    sr.red = false;
+                                xpl.red = true;
+                                root = rotateLeft(root, xpl);
+                                xpl = (xp = x.parent) == null ?
+                                    null : xp.left;
+                            }
+                            if (xpl != null) {
+                                xpl.red = (xp == null) ? false : xp.red;
+                                if ((sl = xpl.left) != null)
+                                    sl.red = false;
+                            }
+                            if (xp != null) {
+                                xp.red = false;
+                                root = rotateRight(root, xp);
+                            }
+                            x = root;
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * Recursive invariant check
+         */
+        static <K,V> boolean checkInvariants(TreeNode<K,V> t) {
+            TreeNode<K,V> tp = t.parent, tl = t.left, tr = t.right,
+                tb = t.prev, tn = (TreeNode<K,V>)t.next;
+            if (tb != null && tb.next != t)
+                return false;
+            if (tn != null && tn.prev != t)
+                return false;
+            if (tp != null && t != tp.left && t != tp.right)
+                return false;
+            if (tl != null && (tl.parent != t || tl.hash > t.hash))
+                return false;
+            if (tr != null && (tr.parent != t || tr.hash < t.hash))
+                return false;
+            if (t.red && tl != null && tl.red && tr != null && tr.red)
+                return false;
+            if (tl != null && !checkInvariants(tl))
+                return false;
+            if (tr != null && !checkInvariants(tr))
+                return false;
+            return true;
+        }
+
+        private static final sun.misc.Unsafe U;
+        private static final long LOCKSTATE;
+        static {
+            try {
+                U = sun.misc.Unsafe.getUnsafe();
+                Class<?> k = TreeBin.class;
+                LOCKSTATE = U.objectFieldOffset
+                    (k.getDeclaredField("lockState"));
+            } catch (Exception e) {
+                throw new Error(e);
+            }
+        }
+    }
+
+    /* ----------------Table Traversal -------------- */
+
+    /**
+     * Encapsulates traversal for methods such as containsValue; also
+     * serves as a base class for other iterators.
+     *
+     * Method advance visits once each still-valid node that was
+     * reachable upon iterator construction. It might miss some that
+     * were added to a bin after the bin was visited, which is OK wrt
+     * consistency guarantees. Maintaining this property in the face
+     * of possible ongoing resizes requires a fair amount of
+     * bookkeeping state that is difficult to optimize away amidst
+     * volatile accesses.  Even so, traversal maintains reasonable
+     * throughput.
+     *
+     * Normally, iteration proceeds bin-by-bin traversing lists.
+     * However, if the table has been resized, then all future steps
+     * must traverse both the bin at the current index as well as at
+     * (index + baseSize); and so on for further resizings. To
+     * paranoically cope with potential sharing by users of iterators
+     * across threads, iteration terminates if a bounds checks fails
+     * for a table read.
+     */
+    static class Traverser<K,V> {
+        Node<K,V>[] tab;        // current table; updated if resized
+        Node<K,V> next;         // the next entry to use
+        int index;              // index of bin to use next
+        int baseIndex;          // current index of initial table
+        int baseLimit;          // index bound for initial table
+        final int baseSize;     // initial table size
+
+        Traverser(Node<K,V>[] tab, int size, int index, int limit) {
+            this.tab = tab;
+            this.baseSize = size;
+            this.baseIndex = this.index = index;
+            this.baseLimit = limit;
+            this.next = null;
+        }
+
+        /**
+         * Advances if possible, returning next valid node, or null if none.
+         */
+        final Node<K,V> advance() {
+            Node<K,V> e;
+            if ((e = next) != null)
+                e = e.next;
+            for (;;) {
+                Node<K,V>[] t; int i, n; K ek;  // must use locals in checks
+                if (e != null)
+                    return next = e;
+                if (baseIndex >= baseLimit || (t = tab) == null ||
+                    (n = t.length) <= (i = index) || i < 0)
+                    return next = null;
+                if ((e = tabAt(t, index)) != null && e.hash < 0) {
+                    if (e instanceof ForwardingNode) {
+                        tab = ((ForwardingNode<K,V>)e).nextTable;
+                        e = null;
+                        continue;
+                    }
+                    else if (e instanceof TreeBin)
+                        e = ((TreeBin<K,V>)e).first;
+                    else
+                        e = null;
+                }
+                if ((index += baseSize) >= n)
+                    index = ++baseIndex;    // visit upper slots if present
+            }
+        }
+    }
+
+    /**
+     * Base of key, value, and entry Iterators. Adds fields to
+     * Traverser to support iterator.remove.
+     */
+    static class BaseIterator<K,V> extends Traverser<K,V> {
+        final ConcurrentHashMap<K,V> map;
+        Node<K,V> lastReturned;
+        BaseIterator(Node<K,V>[] tab, int size, int index, int limit,
+                    ConcurrentHashMap<K,V> map) {
+            super(tab, size, index, limit);
+            this.map = map;
+            advance();
+        }
+
+        public final boolean hasNext() { return next != null; }
+        public final boolean hasMoreElements() { return next != null; }
+
+        public final void remove() {
+            Node<K,V> p;
+            if ((p = lastReturned) == null)
+                throw new IllegalStateException();
+            lastReturned = null;
+            map.replaceNode(p.key, null, null);
+        }
+    }
+
+    static final class KeyIterator<K,V> extends BaseIterator<K,V>
+        implements Iterator<K>, Enumeration<K> {
+        KeyIterator(Node<K,V>[] tab, int index, int size, int limit,
+                    ConcurrentHashMap<K,V> map) {
+            super(tab, index, size, limit, map);
+        }
+
+        public final K next() {
+            Node<K,V> p;
+            if ((p = next) == null)
+                throw new NoSuchElementException();
+            K k = p.key;
+            lastReturned = p;
+            advance();
+            return k;
+        }
+
+        public final K nextElement() { return next(); }
+    }
+
+    static final class ValueIterator<K,V> extends BaseIterator<K,V>
+        implements Iterator<V>, Enumeration<V> {
+        ValueIterator(Node<K,V>[] tab, int index, int size, int limit,
+                      ConcurrentHashMap<K,V> map) {
+            super(tab, index, size, limit, map);
+        }
+
+        public final V next() {
+            Node<K,V> p;
+            if ((p = next) == null)
+                throw new NoSuchElementException();
+            V v = p.val;
+            lastReturned = p;
+            advance();
+            return v;
+        }
+
+        public final V nextElement() { return next(); }
+    }
+
+    static final class EntryIterator<K,V> extends BaseIterator<K,V>
+        implements Iterator<Map.Entry<K,V>> {
+        EntryIterator(Node<K,V>[] tab, int index, int size, int limit,
+                      ConcurrentHashMap<K,V> map) {
+            super(tab, index, size, limit, map);
+        }
+
+        public final Map.Entry<K,V> next() {
+            Node<K,V> p;
+            if ((p = next) == null)
+                throw new NoSuchElementException();
+            K k = p.key;
+            V v = p.val;
+            lastReturned = p;
+            advance();
+            return new MapEntry<K,V>(k, v, map);
+        }
+    }
+
+    /**
+     * Exported Entry for EntryIterator
+     */
+    static final class MapEntry<K,V> implements Map.Entry<K,V> {
+        final K key; // non-null
+        V val;       // non-null
+        final ConcurrentHashMap<K,V> map;
+        MapEntry(K key, V val, ConcurrentHashMap<K,V> map) {
+            this.key = key;
+            this.val = val;
+            this.map = map;
+        }
+        public K getKey()        { return key; }
+        public V getValue()      { return val; }
+        public int hashCode()    { return key.hashCode() ^ val.hashCode(); }
+        public String toString() { return key + "=" + val; }
+
+        public boolean equals(Object o) {
+            Object k, v; Map.Entry<?,?> e;
+            return ((o instanceof Map.Entry) &&
+                    (k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
+                    (v = e.getValue()) != null &&
+                    (k == key || k.equals(key)) &&
+                    (v == val || v.equals(val)));
         }
 
         /**
          * Sets our entry's value and writes through to the map. The
-         * value to return is somewhat arbitrary here. Since a
-         * WriteThroughEntry does not necessarily track asynchronous
-         * changes, the most recent "previous" value could be
-         * different from what we return (or could even have been
-         * removed in which case the put will re-establish). We do not
-         * and cannot guarantee more.
+         * value to return is somewhat arbitrary here. Since we do not
+         * necessarily track asynchronous changes, the most recent
+         * "previous" value could be different from what we return (or
+         * could even have been removed, in which case the put will
+         * re-establish). We do not and cannot guarantee more.
          */
         public V setValue(V value) {
             if (value == null) throw new NullPointerException();
-            V v = super.setValue(value);
-            ConcurrentHashMap.this.put(getKey(), value);
+            V v = val;
+            val = value;
+            map.put(key, value);
             return v;
         }
     }
 
-    final class EntryIterator
-        extends HashIterator
-        implements Iterator<Entry<K,V>>
-    {
-        public Map.Entry<K,V> next() {
-            HashEntry<K,V> e = super.nextEntry();
-            return new WriteThroughEntry(e.key, e.value);
-        }
-    }
-
-    final class KeySet extends AbstractSet<K> {
-        public Iterator<K> iterator() {
-            return new KeyIterator();
-        }
-        public int size() {
-            return ConcurrentHashMap.this.size();
-        }
-        public boolean isEmpty() {
-            return ConcurrentHashMap.this.isEmpty();
-        }
-        public boolean contains(Object o) {
-            return ConcurrentHashMap.this.containsKey(o);
-        }
-        public boolean remove(Object o) {
-            return ConcurrentHashMap.this.remove(o) != null;
-        }
-        public void clear() {
-            ConcurrentHashMap.this.clear();
-        }
-    }
-
-    final class Values extends AbstractCollection<V> {
-        public Iterator<V> iterator() {
-            return new ValueIterator();
-        }
-        public int size() {
-            return ConcurrentHashMap.this.size();
-        }
-        public boolean isEmpty() {
-            return ConcurrentHashMap.this.isEmpty();
-        }
-        public boolean contains(Object o) {
-            return ConcurrentHashMap.this.containsValue(o);
-        }
-        public void clear() {
-            ConcurrentHashMap.this.clear();
-        }
-    }
-
-    final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
-        public Iterator<Map.Entry<K,V>> iterator() {
-            return new EntryIterator();
-        }
-        public boolean contains(Object o) {
-            if (!(o instanceof Map.Entry))
-                return false;
-            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
-            V v = ConcurrentHashMap.this.get(e.getKey());
-            return v != null && v.equals(e.getValue());
-        }
-        public boolean remove(Object o) {
-            if (!(o instanceof Map.Entry))
-                return false;
-            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
-            return ConcurrentHashMap.this.remove(e.getKey(), e.getValue());
-        }
-        public int size() {
-            return ConcurrentHashMap.this.size();
-        }
-        public boolean isEmpty() {
-            return ConcurrentHashMap.this.isEmpty();
-        }
-        public void clear() {
-            ConcurrentHashMap.this.clear();
-        }
-    }
-
-    /* ---------------- Serialization Support -------------- */
+    /* ----------------Views -------------- */
 
     /**
-     * Saves the state of the <tt>ConcurrentHashMap</tt> instance to a
-     * stream (i.e., serializes it).
-     * @param s the stream
-     * @serialData
-     * the key (Object) and value (Object)
-     * for each key-value mapping, followed by a null pair.
-     * The key-value mappings are emitted in no particular order.
+     * Base class for views.
+     *
      */
-    private void writeObject(java.io.ObjectOutputStream s)
-            throws java.io.IOException {
-        // force all segments for serialization compatibility
-        for (int k = 0; k < segments.length; ++k)
-            ensureSegment(k);
-        s.defaultWriteObject();
+    abstract static class CollectionView<K,V,E>
+        implements Collection<E>, java.io.Serializable {
+        private static final long serialVersionUID = 7249069246763182397L;
+        final ConcurrentHashMap<K,V> map;
+        CollectionView(ConcurrentHashMap<K,V> map)  { this.map = map; }
 
-        final Segment<K,V>[] segments = this.segments;
-        for (int k = 0; k < segments.length; ++k) {
-            Segment<K,V> seg = segmentAt(segments, k);
-            seg.lock();
-            try {
-                HashEntry<K,V>[] tab = seg.table;
-                for (int i = 0; i < tab.length; ++i) {
-                    HashEntry<K,V> e;
-                    for (e = entryAt(tab, i); e != null; e = e.next) {
-                        s.writeObject(e.key);
-                        s.writeObject(e.value);
+        /**
+         * Returns the map backing this view.
+         *
+         * @return the map backing this view
+         */
+        public ConcurrentHashMap<K,V> getMap() { return map; }
+
+        /**
+         * Removes all of the elements from this view, by removing all
+         * the mappings from the map backing this view.
+         */
+        public final void clear()      { map.clear(); }
+        public final int size()        { return map.size(); }
+        public final boolean isEmpty() { return map.isEmpty(); }
+
+        // implementations below rely on concrete classes supplying these
+        // abstract methods
+        /**
+         * Returns a "weakly consistent" iterator that will never
+         * throw {@link ConcurrentModificationException}, and
+         * guarantees to traverse elements as they existed upon
+         * construction of the iterator, and may (but is not
+         * guaranteed to) reflect any modifications subsequent to
+         * construction.
+         */
+        public abstract Iterator<E> iterator();
+        public abstract boolean contains(Object o);
+        public abstract boolean remove(Object o);
+
+        private static final String oomeMsg = "Required array size too large";
+
+        public final Object[] toArray() {
+            long sz = map.mappingCount();
+            if (sz > MAX_ARRAY_SIZE)
+                throw new OutOfMemoryError(oomeMsg);
+            int n = (int)sz;
+            Object[] r = new Object[n];
+            int i = 0;
+            for (E e : this) {
+                if (i == n) {
+                    if (n >= MAX_ARRAY_SIZE)
+                        throw new OutOfMemoryError(oomeMsg);
+                    if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1)
+                        n = MAX_ARRAY_SIZE;
+                    else
+                        n += (n >>> 1) + 1;
+                    r = Arrays.copyOf(r, n);
+                }
+                r[i++] = e;
+            }
+            return (i == n) ? r : Arrays.copyOf(r, i);
+        }
+
+        @SuppressWarnings("unchecked")
+        public final <T> T[] toArray(T[] a) {
+            long sz = map.mappingCount();
+            if (sz > MAX_ARRAY_SIZE)
+                throw new OutOfMemoryError(oomeMsg);
+            int m = (int)sz;
+            T[] r = (a.length >= m) ? a :
+                (T[])java.lang.reflect.Array
+                .newInstance(a.getClass().getComponentType(), m);
+            int n = r.length;
+            int i = 0;
+            for (E e : this) {
+                if (i == n) {
+                    if (n >= MAX_ARRAY_SIZE)
+                        throw new OutOfMemoryError(oomeMsg);
+                    if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1)
+                        n = MAX_ARRAY_SIZE;
+                    else
+                        n += (n >>> 1) + 1;
+                    r = Arrays.copyOf(r, n);
+                }
+                r[i++] = (T)e;
+            }
+            if (a == r && i < n) {
+                r[i] = null; // null-terminate
+                return r;
+            }
+            return (i == n) ? r : Arrays.copyOf(r, i);
+        }
+
+        /**
+         * Returns a string representation of this collection.
+         * The string representation consists of the string representations
+         * of the collection's elements in the order they are returned by
+         * its iterator, enclosed in square brackets ({@code "[]"}).
+         * Adjacent elements are separated by the characters {@code ", "}
+         * (comma and space).  Elements are converted to strings as by
+         * {@link String#valueOf(Object)}.
+         *
+         * @return a string representation of this collection
+         */
+        public final String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append('[');
+            Iterator<E> it = iterator();
+            if (it.hasNext()) {
+                for (;;) {
+                    Object e = it.next();
+                    sb.append(e == this ? "(this Collection)" : e);
+                    if (!it.hasNext())
+                        break;
+                    sb.append(',').append(' ');
+                }
+            }
+            return sb.append(']').toString();
+        }
+
+        public final boolean containsAll(Collection<?> c) {
+            if (c != this) {
+                for (Object e : c) {
+                    if (e == null || !contains(e))
+                        return false;
+                }
+            }
+            return true;
+        }
+
+        public final boolean removeAll(Collection<?> c) {
+            boolean modified = false;
+            for (Iterator<E> it = iterator(); it.hasNext();) {
+                if (c.contains(it.next())) {
+                    it.remove();
+                    modified = true;
+                }
+            }
+            return modified;
+        }
+
+        public final boolean retainAll(Collection<?> c) {
+            boolean modified = false;
+            for (Iterator<E> it = iterator(); it.hasNext();) {
+                if (!c.contains(it.next())) {
+                    it.remove();
+                    modified = true;
+                }
+            }
+            return modified;
+        }
+
+    }
+
+    /**
+     * A view of a ConcurrentHashMap as a {@link Set} of keys, in
+     * which additions may optionally be enabled by mapping to a
+     * common value.  This class cannot be directly instantiated.
+     * See {@link #keySet() keySet()},
+     * {@link #keySet(Object) keySet(V)},
+     * {@link #newKeySet() newKeySet()},
+     * {@link #newKeySet(int) newKeySet(int)}.
+     *
+     * @since 1.8
+     *
+     * @hide
+     */
+    public static class KeySetView<K,V> extends CollectionView<K,V,K>
+        implements Set<K>, java.io.Serializable {
+        private static final long serialVersionUID = 7249069246763182397L;
+        private final V value;
+        KeySetView(ConcurrentHashMap<K,V> map, V value) {  // non-public
+            super(map);
+            this.value = value;
+        }
+
+        /**
+         * Returns the default mapped value for additions,
+         * or {@code null} if additions are not supported.
+         *
+         * @return the default mapped value for additions, or {@code null}
+         * if not supported
+         */
+        public V getMappedValue() { return value; }
+
+        /**
+         * {@inheritDoc}
+         * @throws NullPointerException if the specified key is null
+         */
+        public boolean contains(Object o) { return map.containsKey(o); }
+
+        /**
+         * Removes the key from this map view, by removing the key (and its
+         * corresponding value) from the backing map.  This method does
+         * nothing if the key is not in the map.
+         *
+         * @param  o the key to be removed from the backing map
+         * @return {@code true} if the backing map contained the specified key
+         * @throws NullPointerException if the specified key is null
+         */
+        public boolean remove(Object o) { return map.remove(o) != null; }
+
+        /**
+         * @return an iterator over the keys of the backing map
+         */
+        public Iterator<K> iterator() {
+            Node<K,V>[] t;
+            ConcurrentHashMap<K,V> m = map;
+            int f = (t = m.table) == null ? 0 : t.length;
+            return new KeyIterator<K,V>(t, f, 0, f, m);
+        }
+
+        /**
+         * Adds the specified key to this set view by mapping the key to
+         * the default mapped value in the backing map, if defined.
+         *
+         * @param e key to be added
+         * @return {@code true} if this set changed as a result of the call
+         * @throws NullPointerException if the specified key is null
+         * @throws UnsupportedOperationException if no default mapped value
+         * for additions was provided
+         */
+        public boolean add(K e) {
+            V v;
+            if ((v = value) == null)
+                throw new UnsupportedOperationException();
+            return map.putVal(e, v, true) == null;
+        }
+
+        /**
+         * Adds all of the elements in the specified collection to this set,
+         * as if by calling {@link #add} on each one.
+         *
+         * @param c the elements to be inserted into this set
+         * @return {@code true} if this set changed as a result of the call
+         * @throws NullPointerException if the collection or any of its
+         * elements are {@code null}
+         * @throws UnsupportedOperationException if no default mapped value
+         * for additions was provided
+         */
+        public boolean addAll(Collection<? extends K> c) {
+            boolean added = false;
+            V v;
+            if ((v = value) == null)
+                throw new UnsupportedOperationException();
+            for (K e : c) {
+                if (map.putVal(e, v, true) == null)
+                    added = true;
+            }
+            return added;
+        }
+
+        public int hashCode() {
+            int h = 0;
+            for (K e : this)
+                h += e.hashCode();
+            return h;
+        }
+
+        public boolean equals(Object o) {
+            Set<?> c;
+            return ((o instanceof Set) &&
+                    ((c = (Set<?>)o) == this ||
+                     (containsAll(c) && c.containsAll(this))));
+        }
+
+    }
+
+    /**
+     * A view of a ConcurrentHashMap as a {@link Collection} of
+     * values, in which additions are disabled. This class cannot be
+     * directly instantiated. See {@link #values()}.
+     */
+    static final class ValuesView<K,V> extends CollectionView<K,V,V>
+        implements Collection<V>, java.io.Serializable {
+        private static final long serialVersionUID = 2249069246763182397L;
+        ValuesView(ConcurrentHashMap<K,V> map) { super(map); }
+        public final boolean contains(Object o) {
+            return map.containsValue(o);
+        }
+
+        public final boolean remove(Object o) {
+            if (o != null) {
+                for (Iterator<V> it = iterator(); it.hasNext();) {
+                    if (o.equals(it.next())) {
+                        it.remove();
+                        return true;
                     }
                 }
-            } finally {
-                seg.unlock();
             }
+            return false;
         }
-        s.writeObject(null);
-        s.writeObject(null);
+
+        public final Iterator<V> iterator() {
+            ConcurrentHashMap<K,V> m = map;
+            Node<K,V>[] t;
+            int f = (t = m.table) == null ? 0 : t.length;
+            return new ValueIterator<K,V>(t, f, 0, f, m);
+        }
+
+        public final boolean add(V e) {
+            throw new UnsupportedOperationException();
+        }
+        public final boolean addAll(Collection<? extends V> c) {
+            throw new UnsupportedOperationException();
+        }
+
     }
 
     /**
-     * Reconstitutes the <tt>ConcurrentHashMap</tt> instance from a
-     * stream (i.e., deserializes it).
-     * @param s the stream
+     * A view of a ConcurrentHashMap as a {@link Set} of (key, value)
+     * entries.  This class cannot be directly instantiated. See
+     * {@link #entrySet()}.
      */
-    @SuppressWarnings("unchecked")
-    private void readObject(java.io.ObjectInputStream s)
-            throws java.io.IOException, ClassNotFoundException {
-        s.defaultReadObject();
+    static final class EntrySetView<K,V> extends CollectionView<K,V,Map.Entry<K,V>>
+        implements Set<Map.Entry<K,V>>, java.io.Serializable {
+        private static final long serialVersionUID = 2249069246763182397L;
+        EntrySetView(ConcurrentHashMap<K,V> map) { super(map); }
 
-        // Re-initialize segments to be minimally sized, and let grow.
-        int cap = MIN_SEGMENT_TABLE_CAPACITY;
-        final Segment<K,V>[] segments = this.segments;
-        for (int k = 0; k < segments.length; ++k) {
-            Segment<K,V> seg = segments[k];
-            if (seg != null) {
-                seg.threshold = (int)(cap * seg.loadFactor);
-                seg.table = (HashEntry<K,V>[]) new HashEntry<?,?>[cap];
+        public boolean contains(Object o) {
+            Object k, v, r; Map.Entry<?,?> e;
+            return ((o instanceof Map.Entry) &&
+                    (k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
+                    (r = map.get(k)) != null &&
+                    (v = e.getValue()) != null &&
+                    (v == r || v.equals(r)));
+        }
+
+        public boolean remove(Object o) {
+            Object k, v; Map.Entry<?,?> e;
+            return ((o instanceof Map.Entry) &&
+                    (k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
+                    (v = e.getValue()) != null &&
+                    map.remove(k, v));
+        }
+
+        /**
+         * @return an iterator over the entries of the backing map
+         */
+        public Iterator<Map.Entry<K,V>> iterator() {
+            ConcurrentHashMap<K,V> m = map;
+            Node<K,V>[] t;
+            int f = (t = m.table) == null ? 0 : t.length;
+            return new EntryIterator<K,V>(t, f, 0, f, m);
+        }
+
+        public boolean add(Entry<K,V> e) {
+            return map.putVal(e.getKey(), e.getValue(), false) == null;
+        }
+
+        public boolean addAll(Collection<? extends Entry<K,V>> c) {
+            boolean added = false;
+            for (Entry<K,V> e : c) {
+                if (add(e))
+                    added = true;
+            }
+            return added;
+        }
+
+        public final int hashCode() {
+            int h = 0;
+            Node<K,V>[] t;
+            if ((t = map.table) != null) {
+                Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+                for (Node<K,V> p; (p = it.advance()) != null; ) {
+                    h += p.hashCode();
+                }
+            }
+            return h;
+        }
+
+        public final boolean equals(Object o) {
+            Set<?> c;
+            return ((o instanceof Set) &&
+                    ((c = (Set<?>)o) == this ||
+                     (containsAll(c) && c.containsAll(this))));
+        }
+
+    }
+
+
+    /* ---------------- Counters -------------- */
+
+    // Adapted from LongAdder and Striped64.
+    // See their internal docs for explanation.
+
+    // A padded cell for distributing counts
+    static final class CounterCell {
+        volatile long p0, p1, p2, p3, p4, p5, p6;
+        volatile long value;
+        volatile long q0, q1, q2, q3, q4, q5, q6;
+        CounterCell(long x) { value = x; }
+    }
+
+    /**
+     * Holder for the thread-local hash code determining which
+     * CounterCell to use. The code is initialized via the
+     * counterHashCodeGenerator, but may be moved upon collisions.
+     */
+    static final class CounterHashCode {
+        int code;
+    }
+
+    /**
+     * Generates initial value for per-thread CounterHashCodes.
+     */
+    static final AtomicInteger counterHashCodeGenerator = new AtomicInteger();
+
+    /**
+     * Increment for counterHashCodeGenerator. See class ThreadLocal
+     * for explanation.
+     */
+    static final int SEED_INCREMENT = 0x61c88647;
+
+    /**
+     * Per-thread counter hash codes. Shared across all instances.
+     */
+    static final ThreadLocal<CounterHashCode> threadCounterHashCode =
+        new ThreadLocal<CounterHashCode>();
+
+    final long sumCount() {
+        CounterCell[] as = counterCells; CounterCell a;
+        long sum = baseCount;
+        if (as != null) {
+            for (int i = 0; i < as.length; ++i) {
+                if ((a = as[i]) != null)
+                    sum += a.value;
             }
         }
+        return sum;
+    }
 
-        // Read the keys and values, and put the mappings in the table
-        for (;;) {
-            K key = (K) s.readObject();
-            V value = (V) s.readObject();
-            if (key == null)
-                break;
-            put(key, value);
+    // See LongAdder version for explanation
+    private final void fullAddCount(long x, CounterHashCode hc,
+                                    boolean wasUncontended) {
+        int h;
+        if (hc == null) {
+            hc = new CounterHashCode();
+            int s = counterHashCodeGenerator.addAndGet(SEED_INCREMENT);
+            h = hc.code = (s == 0) ? 1 : s; // Avoid zero
+            threadCounterHashCode.set(hc);
         }
+        else
+            h = hc.code;
+        boolean collide = false;                // True if last slot nonempty
+        for (;;) {
+            CounterCell[] as; CounterCell a; int n; long v;
+            if ((as = counterCells) != null && (n = as.length) > 0) {
+                if ((a = as[(n - 1) & h]) == null) {
+                    if (cellsBusy == 0) {            // Try to attach new Cell
+                        CounterCell r = new CounterCell(x); // Optimistic create
+                        if (cellsBusy == 0 &&
+                            U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
+                            boolean created = false;
+                            try {               // Recheck under lock
+                                CounterCell[] rs; int m, j;
+                                if ((rs = counterCells) != null &&
+                                    (m = rs.length) > 0 &&
+                                    rs[j = (m - 1) & h] == null) {
+                                    rs[j] = r;
+                                    created = true;
+                                }
+                            } finally {
+                                cellsBusy = 0;
+                            }
+                            if (created)
+                                break;
+                            continue;           // Slot is now non-empty
+                        }
+                    }
+                    collide = false;
+                }
+                else if (!wasUncontended)       // CAS already known to fail
+                    wasUncontended = true;      // Continue after rehash
+                else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
+                    break;
+                else if (counterCells != as || n >= NCPU)
+                    collide = false;            // At max size or stale
+                else if (!collide)
+                    collide = true;
+                else if (cellsBusy == 0 &&
+                         U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
+                    try {
+                        if (counterCells == as) {// Expand table unless stale
+                            CounterCell[] rs = new CounterCell[n << 1];
+                            for (int i = 0; i < n; ++i)
+                                rs[i] = as[i];
+                            counterCells = rs;
+                        }
+                    } finally {
+                        cellsBusy = 0;
+                    }
+                    collide = false;
+                    continue;                   // Retry with expanded table
+                }
+                h ^= h << 13;                   // Rehash
+                h ^= h >>> 17;
+                h ^= h << 5;
+            }
+            else if (cellsBusy == 0 && counterCells == as &&
+                     U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
+                boolean init = false;
+                try {                           // Initialize table
+                    if (counterCells == as) {
+                        CounterCell[] rs = new CounterCell[2];
+                        rs[h & 1] = new CounterCell(x);
+                        counterCells = rs;
+                        init = true;
+                    }
+                } finally {
+                    cellsBusy = 0;
+                }
+                if (init)
+                    break;
+            }
+            else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
+                break;                          // Fall back on using base
+        }
+        hc.code = h;                            // Record index for next time
     }
 
     // Unsafe mechanics
-    private static final sun.misc.Unsafe UNSAFE;
-    private static final long SBASE;
-    private static final int SSHIFT;
-    private static final long TBASE;
-    private static final int TSHIFT;
+    private static final sun.misc.Unsafe U;
+    private static final long SIZECTL;
+    private static final long TRANSFERINDEX;
+    private static final long TRANSFERORIGIN;
+    private static final long BASECOUNT;
+    private static final long CELLSBUSY;
+    private static final long CELLVALUE;
+    private static final long ABASE;
+    private static final int ASHIFT;
 
     static {
-        int ss, ts;
         try {
-            UNSAFE = sun.misc.Unsafe.getUnsafe();
-            Class<?> tc = HashEntry[].class;
-            Class<?> sc = Segment[].class;
-            TBASE = UNSAFE.arrayBaseOffset(tc);
-            SBASE = UNSAFE.arrayBaseOffset(sc);
-            ts = UNSAFE.arrayIndexScale(tc);
-            ss = UNSAFE.arrayIndexScale(sc);
+            U = sun.misc.Unsafe.getUnsafe();
+            Class<?> k = ConcurrentHashMap.class;
+            SIZECTL = U.objectFieldOffset
+                (k.getDeclaredField("sizeCtl"));
+            TRANSFERINDEX = U.objectFieldOffset
+                (k.getDeclaredField("transferIndex"));
+            TRANSFERORIGIN = U.objectFieldOffset
+                (k.getDeclaredField("transferOrigin"));
+            BASECOUNT = U.objectFieldOffset
+                (k.getDeclaredField("baseCount"));
+            CELLSBUSY = U.objectFieldOffset
+                (k.getDeclaredField("cellsBusy"));
+            Class<?> ck = CounterCell.class;
+            CELLVALUE = U.objectFieldOffset
+                (ck.getDeclaredField("value"));
+            Class<?> ak = Node[].class;
+            ABASE = U.arrayBaseOffset(ak);
+            int scale = U.arrayIndexScale(ak);
+            if ((scale & (scale - 1)) != 0)
+                throw new Error("data type scale not a power of two");
+            ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
         } catch (Exception e) {
             throw new Error(e);
         }
-        if ((ss & (ss-1)) != 0 || (ts & (ts-1)) != 0)
-            throw new Error("data type scale not a power of two");
-        SSHIFT = 31 - Integer.numberOfLeadingZeros(ss);
-        TSHIFT = 31 - Integer.numberOfLeadingZeros(ts);
     }
 
 }
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
index 873f825..b39a533 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
@@ -31,7 +31,7 @@
  * Like most other concurrent collection implementations, this class
  * does not permit the use of {@code null} elements.
  *
- * <p>This implementation employs an efficient &quot;wait-free&quot;
+ * <p>This implementation employs an efficient <em>non-blocking</em>
  * algorithm based on one described in <a
  * href="http://www.cs.rochester.edu/u/michael/PODC96.html"> Simple,
  * Fast, and Practical Non-Blocking and Blocking Concurrent Queue
diff --git a/luni/src/main/java/java/util/concurrent/CountedCompleter.java b/luni/src/main/java/java/util/concurrent/CountedCompleter.java
index ffe7582..d5f794e 100644
--- a/luni/src/main/java/java/util/concurrent/CountedCompleter.java
+++ b/luni/src/main/java/java/util/concurrent/CountedCompleter.java
@@ -8,14 +8,15 @@
 
 /**
  * A {@link ForkJoinTask} with a completion action performed when
- * triggered and there are no remaining pending
- * actions. CountedCompleters are in general more robust in the
+ * triggered and there are no remaining pending actions.
+ * CountedCompleters are in general more robust in the
  * presence of subtask stalls and blockage than are other forms of
  * ForkJoinTasks, but are less intuitive to program.  Uses of
  * CountedCompleter are similar to those of other completion based
  * components (such as {@link java.nio.channels.CompletionHandler})
  * except that multiple <em>pending</em> completions may be necessary
- * to trigger the completion action {@link #onCompletion}, not just one.
+ * to trigger the completion action {@link #onCompletion(CountedCompleter)},
+ * not just one.
  * Unless initialized otherwise, the {@linkplain #getPendingCount pending
  * count} starts at zero, but may be (atomically) changed using
  * methods {@link #setPendingCount}, {@link #addToPendingCount}, and
@@ -40,9 +41,10 @@
  * <p>A concrete CountedCompleter class must define method {@link
  * #compute}, that should in most cases (as illustrated below), invoke
  * {@code tryComplete()} once before returning. The class may also
- * optionally override method {@link #onCompletion} to perform an
- * action upon normal completion, and method {@link
- * #onExceptionalCompletion} to perform an action upon any exception.
+ * optionally override method {@link #onCompletion(CountedCompleter)}
+ * to perform an action upon normal completion, and method
+ * {@link #onExceptionalCompletion(Throwable, CountedCompleter)} to
+ * perform an action upon any exception.
  *
  * <p>CountedCompleters most often do not bear results, in which case
  * they are normally declared as {@code CountedCompleter<Void>}, and
@@ -63,13 +65,14 @@
  * only as an internal helper for other computations, so its own task
  * status (as reported in methods such as {@link ForkJoinTask#isDone})
  * is arbitrary; this status changes only upon explicit invocations of
- * {@link #complete}, {@link ForkJoinTask#cancel}, {@link
- * ForkJoinTask#completeExceptionally} or upon exceptional completion
- * of method {@code compute}. Upon any exceptional completion, the
- * exception may be relayed to a task's completer (and its completer,
- * and so on), if one exists and it has not otherwise already
- * completed. Similarly, cancelling an internal CountedCompleter has
- * only a local effect on that completer, so is not often useful.
+ * {@link #complete}, {@link ForkJoinTask#cancel},
+ * {@link ForkJoinTask#completeExceptionally(Throwable)} or upon
+ * exceptional completion of method {@code compute}. Upon any
+ * exceptional completion, the exception may be relayed to a task's
+ * completer (and its completer, and so on), if one exists and it has
+ * not otherwise already completed. Similarly, cancelling an internal
+ * CountedCompleter has only a local effect on that completer, so is
+ * not often useful.
  *
  * <p><b>Sample Usages.</b>
  *
@@ -96,8 +99,8 @@
  * improve load balancing. In the recursive case, the second of each
  * pair of subtasks to finish triggers completion of its parent
  * (because no result combination is performed, the default no-op
- * implementation of method {@code onCompletion} is not overridden). A
- * static utility method sets up the base task and invokes it
+ * implementation of method {@code onCompletion} is not overridden).
+ * A static utility method sets up the base task and invokes it
  * (here, implicitly using the {@link ForkJoinPool#commonPool()}).
  *
  * <pre> {@code
@@ -152,12 +155,11 @@
  *   }
  * }</pre>
  *
- * As a further improvement, notice that the left task need not even
- * exist.  Instead of creating a new one, we can iterate using the
- * original task, and add a pending count for each fork. Additionally,
- * because no task in this tree implements an {@link #onCompletion}
- * method, {@code tryComplete()} can be replaced with {@link
- * #propagateCompletion}.
+ * As a further improvement, notice that the left task need not even exist.
+ * Instead of creating a new one, we can iterate using the original task,
+ * and add a pending count for each fork.  Additionally, because no task
+ * in this tree implements an {@link #onCompletion(CountedCompleter)} method,
+ * {@code tryComplete()} can be replaced with {@link #propagateCompletion}.
  *
  * <pre> {@code
  * class ForEach<E> ...
@@ -235,7 +237,7 @@
  *
  * <p><b>Recording subtasks.</b> CountedCompleter tasks that combine
  * results of multiple subtasks usually need to access these results
- * in method {@link #onCompletion}. As illustrated in the following
+ * in method {@link #onCompletion(CountedCompleter)}. As illustrated in the following
  * class (that performs a simplified form of map-reduce where mappings
  * and reductions are all of type {@code E}), one way to do this in
  * divide and conquer designs is to have each subtask record its
@@ -357,7 +359,7 @@
  *
  * <p><b>Triggers.</b> Some CountedCompleters are themselves never
  * forked, but instead serve as bits of plumbing in other designs;
- * including those in which the completion of one of more async tasks
+ * including those in which the completion of one or more async tasks
  * triggers another async task. For example:
  *
  * <pre> {@code
@@ -438,20 +440,21 @@
     }
 
     /**
-     * Performs an action when method {@link #completeExceptionally}
-     * is invoked or method {@link #compute} throws an exception, and
-     * this task has not otherwise already completed normally. On
-     * entry to this method, this task {@link
-     * ForkJoinTask#isCompletedAbnormally}.  The return value of this
-     * method controls further propagation: If {@code true} and this
-     * task has a completer, then this completer is also completed
-     * exceptionally.  The default implementation of this method does
-     * nothing except return {@code true}.
+     * Performs an action when method {@link
+     * #completeExceptionally(Throwable)} is invoked or method {@link
+     * #compute} throws an exception, and this task has not already
+     * otherwise completed normally. On entry to this method, this task
+     * {@link ForkJoinTask#isCompletedAbnormally}.  The return value
+     * of this method controls further propagation: If {@code true}
+     * and this task has a completer that has not completed, then that
+     * completer is also completed exceptionally, with the same
+     * exception as this completer.  The default implementation of
+     * this method does nothing except return {@code true}.
      *
      * @param ex the exception
      * @param caller the task invoking this method (which may
      * be this task itself)
-     * @return true if this exception should be propagated to this
+     * @return {@code true} if this exception should be propagated to this
      * task's completer, if one exists
      */
     public boolean onExceptionalCompletion(Throwable ex, CountedCompleter<?> caller) {
@@ -492,7 +495,7 @@
      * @param delta the value to add
      */
     public final void addToPendingCount(int delta) {
-        int c; // note: can replace with intrinsic in jdk8
+        int c;
         do {} while (!U.compareAndSwapInt(this, PENDING, c = pending, c+delta));
     }
 
@@ -502,7 +505,7 @@
      *
      * @param expected the expected value
      * @param count the new value
-     * @return true if successful
+     * @return {@code true} if successful
      */
     public final boolean compareAndSetPendingCount(int expected, int count) {
         return U.compareAndSwapInt(this, PENDING, expected, count);
@@ -536,9 +539,9 @@
 
     /**
      * If the pending count is nonzero, decrements the count;
-     * otherwise invokes {@link #onCompletion} and then similarly
-     * tries to complete this task's completer, if one exists,
-     * else marks this task as complete.
+     * otherwise invokes {@link #onCompletion(CountedCompleter)}
+     * and then similarly tries to complete this task's completer,
+     * if one exists, else marks this task as complete.
      */
     public final void tryComplete() {
         CountedCompleter<?> a = this, s = a;
@@ -557,12 +560,12 @@
 
     /**
      * Equivalent to {@link #tryComplete} but does not invoke {@link
-     * #onCompletion} along the completion path: If the pending count
-     * is nonzero, decrements the count; otherwise, similarly tries to
-     * complete this task's completer, if one exists, else marks this
-     * task as complete. This method may be useful in cases where
-     * {@code onCompletion} should not, or need not, be invoked for
-     * each completer in a computation.
+     * #onCompletion(CountedCompleter)} along the completion path:
+     * If the pending count is nonzero, decrements the count;
+     * otherwise, similarly tries to complete this task's completer, if
+     * one exists, else marks this task as complete. This method may be
+     * useful in cases where {@code onCompletion} should not, or need
+     * not, be invoked for each completer in a computation.
      */
     public final void propagateCompletion() {
         CountedCompleter<?> a = this, s = a;
@@ -579,13 +582,15 @@
     }
 
     /**
-     * Regardless of pending count, invokes {@link #onCompletion},
-     * marks this task as complete and further triggers {@link
-     * #tryComplete} on this task's completer, if one exists.  The
-     * given rawResult is used as an argument to {@link #setRawResult}
-     * before invoking {@link #onCompletion} or marking this task as
-     * complete; its value is meaningful only for classes overriding
-     * {@code setRawResult}.
+     * Regardless of pending count, invokes
+     * {@link #onCompletion(CountedCompleter)}, marks this task as
+     * complete and further triggers {@link #tryComplete} on this
+     * task's completer, if one exists.  The given rawResult is
+     * used as an argument to {@link #setRawResult} before invoking
+     * {@link #onCompletion(CountedCompleter)} or marking this task
+     * as complete; its value is meaningful only for classes
+     * overriding {@code setRawResult}.  This method does not modify
+     * the pending count.
      *
      * <p>This method may be useful when forcing completion as soon as
      * any one (versus all) of several subtask results are obtained.
@@ -604,7 +609,6 @@
             p.tryComplete();
     }
 
-
     /**
      * If this task's pending count is zero, returns this task;
      * otherwise decrements its pending count and returns {@code
@@ -668,8 +672,9 @@
     void internalPropagateException(Throwable ex) {
         CountedCompleter<?> a = this, s = a;
         while (a.onExceptionalCompletion(ex, s) &&
-               (a = (s = a).completer) != null && a.status >= 0)
-            a.recordExceptionalCompletion(ex);
+               (a = (s = a).completer) != null && a.status >= 0 &&
+               a.recordExceptionalCompletion(ex) == EXCEPTIONAL)
+            ;
     }
 
     /**
diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinPool.java b/luni/src/main/java/java/util/concurrent/ForkJoinPool.java
index 87ffff3..5ac01c8 100644
--- a/luni/src/main/java/java/util/concurrent/ForkJoinPool.java
+++ b/luni/src/main/java/java/util/concurrent/ForkJoinPool.java
@@ -6,6 +6,7 @@
 
 package java.util.concurrent;
 
+import java.lang.Thread.UncaughtExceptionHandler;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -17,6 +18,7 @@
 import java.util.concurrent.Future;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -49,9 +51,9 @@
  * level; by default, equal to the number of available processors. The
  * pool attempts to maintain enough active (or available) threads by
  * dynamically adding, suspending, or resuming internal worker
- * threads, even if some tasks are stalled waiting to join
- * others. However, no such adjustments are guaranteed in the face of
- * blocked I/O or other unmanaged synchronization. The nested {@link
+ * threads, even if some tasks are stalled waiting to join others.
+ * However, no such adjustments are guaranteed in the face of blocked
+ * I/O or other unmanaged synchronization. The nested {@link
  * ManagedBlocker} interface enables extension of the kinds of
  * synchronization accommodated.
  *
@@ -75,38 +77,45 @@
  * there is little difference among choice of methods.
  *
  * <table BORDER CELLPADDING=3 CELLSPACING=1>
+ * <caption>Summary of task execution methods</caption>
  *  <tr>
  *    <td></td>
  *    <td ALIGN=CENTER> <b>Call from non-fork/join clients</b></td>
  *    <td ALIGN=CENTER> <b>Call from within fork/join computations</b></td>
  *  </tr>
  *  <tr>
- *    <td> <b>Arrange async execution</td>
+ *    <td> <b>Arrange async execution</b></td>
  *    <td> {@link #execute(ForkJoinTask)}</td>
  *    <td> {@link ForkJoinTask#fork}</td>
  *  </tr>
  *  <tr>
- *    <td> <b>Await and obtain result</td>
+ *    <td> <b>Await and obtain result</b></td>
  *    <td> {@link #invoke(ForkJoinTask)}</td>
  *    <td> {@link ForkJoinTask#invoke}</td>
  *  </tr>
  *  <tr>
- *    <td> <b>Arrange exec and obtain Future</td>
+ *    <td> <b>Arrange exec and obtain Future</b></td>
  *    <td> {@link #submit(ForkJoinTask)}</td>
  *    <td> {@link ForkJoinTask#fork} (ForkJoinTasks <em>are</em> Futures)</td>
  *  </tr>
  * </table>
  *
  * <p>The common pool is by default constructed with default
- * parameters, but these may be controlled by setting three {@link
- * System#getProperty system properties} with prefix {@code
- * java.util.concurrent.ForkJoinPool.common}: {@code parallelism} --
- * an integer greater than zero, {@code threadFactory} -- the class
- * name of a {@link ForkJoinWorkerThreadFactory}, and {@code
- * exceptionHandler} -- the class name of a {@link
- * java.lang.Thread.UncaughtExceptionHandler
- * Thread.UncaughtExceptionHandler}. Upon any error in establishing
- * these settings, default parameters are used.
+ * parameters, but these may be controlled by setting three
+ * {@linkplain System#getProperty system properties}:
+ * <ul>
+ * <li>{@code java.util.concurrent.ForkJoinPool.common.parallelism}
+ * - the parallelism level, a non-negative integer
+ * <li>{@code java.util.concurrent.ForkJoinPool.common.threadFactory}
+ * - the class name of a {@link ForkJoinWorkerThreadFactory}
+ * <li>{@code java.util.concurrent.ForkJoinPool.common.exceptionHandler}
+ * - the class name of a {@link UncaughtExceptionHandler}
+ * </ul>
+ * The system class loader is used to load these classes.
+ * Upon any error in establishing these settings, default parameters
+ * are used. It is possible to disable or limit the use of threads in
+ * the common pool by setting the parallelism property to zero, and/or
+ * using a factory that may return {@code null}.
  *
  * <p><b>Implementation notes</b>: This implementation restricts the
  * maximum number of running threads to 32767. Attempts to create
@@ -153,32 +162,35 @@
      * (http://research.sun.com/scalable/pubs/index.html) and
      * "Idempotent work stealing" by Michael, Saraswat, and Vechev,
      * PPoPP 2009 (http://portal.acm.org/citation.cfm?id=1504186).
-     * The main differences ultimately stem from GC requirements that
-     * we null out taken slots as soon as we can, to maintain as small
-     * a footprint as possible even in programs generating huge
-     * numbers of tasks. To accomplish this, we shift the CAS
-     * arbitrating pop vs poll (steal) from being on the indices
-     * ("base" and "top") to the slots themselves.  So, both a
-     * successful pop and poll mainly entail a CAS of a slot from
-     * non-null to null.  Because we rely on CASes of references, we
-     * do not need tag bits on base or top.  They are simple ints as
-     * used in any circular array-based queue (see for example
-     * ArrayDeque).  Updates to the indices must still be ordered in a
-     * way that guarantees that top == base means the queue is empty,
-     * but otherwise may err on the side of possibly making the queue
-     * appear nonempty when a push, pop, or poll have not fully
-     * committed. Note that this means that the poll operation,
-     * considered individually, is not wait-free. One thief cannot
-     * successfully continue until another in-progress one (or, if
-     * previously empty, a push) completes.  However, in the
-     * aggregate, we ensure at least probabilistic non-blockingness.
-     * If an attempted steal fails, a thief always chooses a different
-     * random victim target to try next. So, in order for one thief to
-     * progress, it suffices for any in-progress poll or new push on
-     * any empty queue to complete. (This is why we normally use
-     * method pollAt and its variants that try once at the apparent
-     * base index, else consider alternative actions, rather than
-     * method poll.)
+     * See also "Correct and Efficient Work-Stealing for Weak Memory
+     * Models" by Le, Pop, Cohen, and Nardelli, PPoPP 2013
+     * (http://www.di.ens.fr/~zappa/readings/ppopp13.pdf) for an
+     * analysis of memory ordering (atomic, volatile etc) issues.  The
+     * main differences ultimately stem from GC requirements that we
+     * null out taken slots as soon as we can, to maintain as small a
+     * footprint as possible even in programs generating huge numbers
+     * of tasks. To accomplish this, we shift the CAS arbitrating pop
+     * vs poll (steal) from being on the indices ("base" and "top") to
+     * the slots themselves.  So, both a successful pop and poll
+     * mainly entail a CAS of a slot from non-null to null.  Because
+     * we rely on CASes of references, we do not need tag bits on base
+     * or top.  They are simple ints as used in any circular
+     * array-based queue (see for example ArrayDeque).  Updates to the
+     * indices must still be ordered in a way that guarantees that top
+     * == base means the queue is empty, but otherwise may err on the
+     * side of possibly making the queue appear nonempty when a push,
+     * pop, or poll have not fully committed. Note that this means
+     * that the poll operation, considered individually, is not
+     * wait-free. One thief cannot successfully continue until another
+     * in-progress one (or, if previously empty, a push) completes.
+     * However, in the aggregate, we ensure at least probabilistic
+     * non-blockingness.  If an attempted steal fails, a thief always
+     * chooses a different random victim target to try next. So, in
+     * order for one thief to progress, it suffices for any
+     * in-progress poll or new push on any empty queue to
+     * complete. (This is why we normally use method pollAt and its
+     * variants that try once at the apparent base index, else
+     * consider alternative actions, rather than method poll.)
      *
      * This approach also enables support of a user mode in which local
      * task processing is in FIFO, not LIFO order, simply by using
@@ -197,18 +209,18 @@
      * for work-stealing (this would contaminate lifo/fifo
      * processing). Instead, we randomly associate submission queues
      * with submitting threads, using a form of hashing.  The
-     * ThreadLocal Submitter class contains a value initially used as
-     * a hash code for choosing existing queues, but may be randomly
-     * repositioned upon contention with other submitters.  In
-     * essence, submitters act like workers except that they are
-     * restricted to executing local tasks that they submitted (or in
-     * the case of CountedCompleters, others with the same root task).
-     * However, because most shared/external queue operations are more
-     * expensive than internal, and because, at steady state, external
-     * submitters will compete for CPU with workers, ForkJoinTask.join
-     * and related methods disable them from repeatedly helping to
-     * process tasks if all workers are active.  Insertion of tasks in
-     * shared mode requires a lock (mainly to protect in the case of
+     * Submitter probe value serves as a hash code for
+     * choosing existing queues, and may be randomly repositioned upon
+     * contention with other submitters.  In essence, submitters act
+     * like workers except that they are restricted to executing local
+     * tasks that they submitted (or in the case of CountedCompleters,
+     * others with the same root task).  However, because most
+     * shared/external queue operations are more expensive than
+     * internal, and because, at steady state, external submitters
+     * will compete for CPU with workers, ForkJoinTask.join and
+     * related methods disable them from repeatedly helping to process
+     * tasks if all workers are active.  Insertion of tasks in shared
+     * mode requires a lock (mainly to protect in the case of
      * resizing) but we use only a simple spinlock (using bits in
      * field qlock), because submitters encountering a busy queue move
      * on to try or create other queues -- they block only when
@@ -298,37 +310,35 @@
      * has not yet entered the wait queue. We solve this by requiring
      * a full sweep of all workers (via repeated calls to method
      * scan()) both before and after a newly waiting worker is added
-     * to the wait queue. During a rescan, the worker might release
-     * some other queued worker rather than itself, which has the same
-     * net effect. Because enqueued workers may actually be rescanning
-     * rather than waiting, we set and clear the "parker" field of
-     * WorkQueues to reduce unnecessary calls to unpark.  (This
-     * requires a secondary recheck to avoid missed signals.)  Note
-     * the unusual conventions about Thread.interrupts surrounding
-     * parking and other blocking: Because interrupts are used solely
-     * to alert threads to check termination, which is checked anyway
-     * upon blocking, we clear status (using Thread.interrupted)
-     * before any call to park, so that park does not immediately
-     * return due to status being set via some other unrelated call to
-     * interrupt in user code.
+     * to the wait queue.  Because enqueued workers may actually be
+     * rescanning rather than waiting, we set and clear the "parker"
+     * field of WorkQueues to reduce unnecessary calls to unpark.
+     * (This requires a secondary recheck to avoid missed signals.)
+     * Note the unusual conventions about Thread.interrupts
+     * surrounding parking and other blocking: Because interrupts are
+     * used solely to alert threads to check termination, which is
+     * checked anyway upon blocking, we clear status (using
+     * Thread.interrupted) before any call to park, so that park does
+     * not immediately return due to status being set via some other
+     * unrelated call to interrupt in user code.
      *
      * Signalling.  We create or wake up workers only when there
      * appears to be at least one task they might be able to find and
-     * execute. However, many other threads may notice the same task
-     * and each signal to wake up a thread that might take it. So in
-     * general, pools will be over-signalled.  When a submission is
-     * added or another worker adds a task to a queue that has fewer
-     * than two tasks, they signal waiting workers (or trigger
-     * creation of new ones if fewer than the given parallelism level
-     * -- signalWork), and may leave a hint to the unparked worker to
-     * help signal others upon wakeup).  These primary signals are
-     * buttressed by others (see method helpSignal) whenever other
-     * threads scan for work or do not have a task to process.  On
-     * most platforms, signalling (unpark) overhead time is noticeably
+     * execute.  When a submission is added or another worker adds a
+     * task to a queue that has fewer than two tasks, they signal
+     * waiting workers (or trigger creation of new ones if fewer than
+     * the given parallelism level -- signalWork).  These primary
+     * signals are buttressed by others whenever other threads remove
+     * a task from a queue and notice that there are other tasks there
+     * as well.  So in general, pools will be over-signalled. On most
+     * platforms, signalling (unpark) overhead time is noticeably
      * long, and the time between signalling a thread and it actually
      * making progress can be very noticeably long, so it is worth
      * offloading these delays from critical paths as much as
-     * possible.
+     * possible. Additionally, workers spin-down gradually, by staying
+     * alive so long as they see the ctl state changing.  Similar
+     * stability-sensing techniques are also used before blocking in
+     * awaitJoin and helpComplete.
      *
      * Trimming workers. To release resources after periods of lack of
      * use, a worker starting to wait when the pool is quiescent will
@@ -441,7 +451,7 @@
      * Common Pool
      * ===========
      *
-     * The static commonPool always exists after static
+     * The static common pool always exists after static
      * initialization.  Since it (or any other created pool) need
      * never be used, we minimize initial construction overhead and
      * footprint to the setup of about a dozen fields, with no nested
@@ -449,8 +459,11 @@
      * fullExternalPush during the first submission to the pool.
      *
      * When external threads submit to the common pool, they can
-     * perform some subtask processing (see externalHelpJoin and
-     * related methods).  We do not need to record whether these
+     * perform subtask processing (see externalHelpJoin and related
+     * methods).  This caller-helps policy makes it sensible to set
+     * common pool parallelism level to one (or more) less than the
+     * total number of available cores, or even zero for pure
+     * caller-runs.  We do not need to record whether external
      * submissions are to the common pool -- if not, externalHelpJoin
      * returns quickly (at the most helping to signal some common pool
      * workers). These submitters would otherwise be blocked waiting
@@ -520,6 +533,7 @@
          *
          * @param pool the pool this thread works in
          * @throws NullPointerException if the pool is null
+         * @return the new worker thread
          */
         public ForkJoinWorkerThread newThread(ForkJoinPool pool);
     }
@@ -536,26 +550,6 @@
     }
 
     /**
-     * Per-thread records for threads that submit to pools. Currently
-     * holds only pseudo-random seed / index that is used to choose
-     * submission queues in method externalPush. In the future, this may
-     * also incorporate a means to implement different task rejection
-     * and resubmission policies.
-     *
-     * Seeds for submitters and workers/workQueues work in basically
-     * the same way but are initialized and updated using slightly
-     * different mechanics. Both are initialized using the same
-     * approach as in class ThreadLocal, where successive values are
-     * unlikely to collide with previous values. Seeds are then
-     * randomly modified upon collisions using xorshifts, which
-     * requires a non-zero seed.
-     */
-    static final class Submitter {
-        int seed;
-        Submitter(int s) { seed = s; }
-    }
-
-    /**
      * Class for artificial tasks that are used to replace the target
      * of local joins if they are removed from an interior queue slot
      * in WorkQueue.tryRemoveAndExec. We don't need the proxy to
@@ -614,17 +608,8 @@
      * do not want multiple WorkQueue instances or multiple queue
      * arrays sharing cache lines. (It would be best for queue objects
      * and their arrays to share, but there is nothing available to
-     * help arrange that).  Unfortunately, because they are recorded
-     * in a common array, WorkQueue instances are often moved to be
-     * adjacent by garbage collectors. To reduce impact, we use field
-     * padding that works OK on common platforms; this effectively
-     * trades off slightly slower average field access for the sake of
-     * avoiding really bad worst-case access. (Until better JVM
-     * support is in place, this padding is dependent on transient
-     * properties of JVM field layout rules.) We also take care in
-     * allocating, sizing and resizing the array. Non-shared queue
-     * arrays are initialized by workers before use. Others are
-     * allocated on first use.
+     * help arrange that). The @Contended annotation alerts JVMs to
+     * try to keep instances apart.
      */
     static final class WorkQueue {
         /**
@@ -650,13 +635,12 @@
         // Heuristic padding to ameliorate unfortunate memory placements
         volatile long pad00, pad01, pad02, pad03, pad04, pad05, pad06;
 
-        int seed;                  // for random scanning; initialize nonzero
         volatile int eventCount;   // encoded inactivation count; < 0 if inactive
         int nextWait;              // encoded record of next event waiter
-        int hint;                  // steal or signal hint (index)
-        int poolIndex;             // index of this queue in pool (or 0)
-        final int mode;            // 0: lifo, > 0: fifo, < 0: shared
         int nsteals;               // number of steals
+        int hint;                  // steal index hint
+        short poolIndex;           // index of this queue in pool
+        final short mode;          // 0: lifo, > 0: fifo, < 0: shared
         volatile int qlock;        // 1: locked, -1: terminate; else 0
         volatile int base;         // index of next slot for poll
         int top;                   // index of next slot for push
@@ -674,8 +658,8 @@
                   int seed) {
             this.pool = pool;
             this.owner = owner;
-            this.mode = mode;
-            this.seed = seed;
+            this.mode = (short)mode;
+            this.hint = seed; // store initial seed for runWorker
             // Place indices in the center of array (that is not yet allocated)
             base = top = INITIAL_QUEUE_CAPACITY >>> 1;
         }
@@ -688,7 +672,7 @@
             return (n >= 0) ? 0 : -n; // ignore transient negative
         }
 
-       /**
+        /**
          * Provides a more accurate estimate of whether this queue has
          * any tasks than does queueSize, by checking whether a
          * near-empty queue has at least one unclaimed task.
@@ -713,20 +697,18 @@
          */
         final void push(ForkJoinTask<?> task) {
             ForkJoinTask<?>[] a; ForkJoinPool p;
-            int s = top, m, n;
+            int s = top, n;
             if ((a = array) != null) {    // ignore if queue removed
-                int j = (((m = a.length - 1) & s) << ASHIFT) + ABASE;
-                U.putOrderedObject(a, j, task);
-                if ((n = (top = s + 1) - base) <= 2) {
-                    if ((p = pool) != null)
-                        p.signalWork(this);
-                }
+                int m = a.length - 1;
+                U.putOrderedObject(a, ((m & s) << ASHIFT) + ABASE, task);
+                if ((n = (top = s + 1) - base) <= 2)
+                    (p = pool).signalWork(p.workQueues, this);
                 else if (n >= m)
                     growArray();
             }
         }
 
-       /**
+        /**
          * Initializes or doubles the capacity of array. Call either
          * by owner or with lock held -- it is OK for base, but not
          * top, to move while resizings are in progress.
@@ -784,9 +766,8 @@
             if ((a = array) != null) {
                 int j = (((a.length - 1) & b) << ASHIFT) + ABASE;
                 if ((t = (ForkJoinTask<?>)U.getObjectVolatile(a, j)) != null &&
-                    base == b &&
-                    U.compareAndSwapObject(a, j, t, null)) {
-                    base = b + 1;
+                    base == b && U.compareAndSwapObject(a, j, t, null)) {
+                    U.putOrderedInt(this, QBASE, b + 1);
                     return t;
                 }
             }
@@ -802,9 +783,8 @@
                 int j = (((a.length - 1) & b) << ASHIFT) + ABASE;
                 t = (ForkJoinTask<?>)U.getObjectVolatile(a, j);
                 if (t != null) {
-                    if (base == b &&
-                        U.compareAndSwapObject(a, j, t, null)) {
-                        base = b + 1;
+                    if (U.compareAndSwapObject(a, j, t, null)) {
+                        U.putOrderedInt(this, QBASE, b + 1);
                         return t;
                     }
                 }
@@ -861,49 +841,46 @@
                 ForkJoinTask.cancelIgnoringExceptions(t);
         }
 
-        /**
-         * Computes next value for random probes.  Scans don't require
-         * a very high quality generator, but also not a crummy one.
-         * Marsaglia xor-shift is cheap and works well enough.  Note:
-         * This is manually inlined in its usages in ForkJoinPool to
-         * avoid writes inside busy scan loops.
-         */
-        final int nextSeed() {
-            int r = seed;
-            r ^= r << 13;
-            r ^= r >>> 17;
-            return seed = r ^= r << 5;
-        }
-
         // Specialized execution methods
 
         /**
-         * Pops and runs tasks until empty.
-         */
-        private void popAndExecAll() {
-            // A bit faster than repeated pop calls
-            ForkJoinTask<?>[] a; int m, s; long j; ForkJoinTask<?> t;
-            while ((a = array) != null && (m = a.length - 1) >= 0 &&
-                   (s = top - 1) - base >= 0 &&
-                   (t = ((ForkJoinTask<?>)
-                         U.getObject(a, j = ((m & s) << ASHIFT) + ABASE)))
-                   != null) {
-                if (U.compareAndSwapObject(a, j, t, null)) {
-                    top = s;
-                    t.doExec();
-                }
-            }
-        }
-
-        /**
          * Polls and runs tasks until empty.
          */
-        private void pollAndExecAll() {
+        final void pollAndExecAll() {
             for (ForkJoinTask<?> t; (t = poll()) != null;)
                 t.doExec();
         }
 
         /**
+         * Executes a top-level task and any local tasks remaining
+         * after execution.
+         */
+        final void runTask(ForkJoinTask<?> task) {
+            if ((currentSteal = task) != null) {
+                task.doExec();
+                ForkJoinTask<?>[] a = array;
+                int md = mode;
+                ++nsteals;
+                currentSteal = null;
+                if (md != 0)
+                    pollAndExecAll();
+                else if (a != null) {
+                    int s, m = a.length - 1;
+                    while ((s = top - 1) - base >= 0) {
+                        long i = ((m & s) << ASHIFT) + ABASE;
+                        ForkJoinTask<?> t = (ForkJoinTask<?>)U.getObject(a, i);
+                        if (t == null)
+                            break;
+                        if (U.compareAndSwapObject(a, i, t, null)) {
+                            top = s;
+                            t.doExec();
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
          * If present, removes from queue and executes the given task,
          * or any other cancelled task. Returns (true) on any CAS
          * or consistency check failure so caller can retry.
@@ -911,13 +888,15 @@
          * @return false if no progress can be made, else true
          */
         final boolean tryRemoveAndExec(ForkJoinTask<?> task) {
-            boolean stat = true, removed = false, empty = true;
+            boolean stat;
             ForkJoinTask<?>[] a; int m, s, b, n;
-            if ((a = array) != null && (m = a.length - 1) >= 0 &&
+            if (task != null && (a = array) != null && (m = a.length - 1) >= 0 &&
                 (n = (s = top) - (b = base)) > 0) {
+                boolean removed = false, empty = true;
+                stat = true;
                 for (ForkJoinTask<?> t;;) {           // traverse from s to b
-                    int j = ((--s & m) << ASHIFT) + ABASE;
-                    t = (ForkJoinTask<?>)U.getObjectVolatile(a, j);
+                    long j = ((--s & m) << ASHIFT) + ABASE;
+                    t = (ForkJoinTask<?>)U.getObject(a, j);
                     if (t == null)                    // inconsistent length
                         break;
                     else if (t == task) {
@@ -945,68 +924,95 @@
                         break;
                     }
                 }
+                if (removed)
+                    task.doExec();
             }
-            if (removed)
-                task.doExec();
+            else
+                stat = false;
             return stat;
         }
 
         /**
-         * Polls for and executes the given task or any other task in
-         * its CountedCompleter computation.
+         * Tries to poll for and execute the given task or any other
+         * task in its CountedCompleter computation.
          */
-        final boolean pollAndExecCC(ForkJoinTask<?> root) {
-            ForkJoinTask<?>[] a; int b; Object o;
-            outer: while ((b = base) - top < 0 && (a = array) != null) {
+        final boolean pollAndExecCC(CountedCompleter<?> root) {
+            ForkJoinTask<?>[] a; int b; Object o; CountedCompleter<?> t, r;
+            if ((b = base) - top < 0 && (a = array) != null) {
                 long j = (((a.length - 1) & b) << ASHIFT) + ABASE;
-                if ((o = U.getObject(a, j)) == null ||
-                    !(o instanceof CountedCompleter))
-                    break;
-                for (CountedCompleter<?> t = (CountedCompleter<?>)o, r = t;;) {
-                    if (r == root) {
-                        if (base == b &&
-                            U.compareAndSwapObject(a, j, t, null)) {
-                            base = b + 1;
-                            t.doExec();
+                if ((o = U.getObjectVolatile(a, j)) == null)
+                    return true; // retry
+                if (o instanceof CountedCompleter) {
+                    for (t = (CountedCompleter<?>)o, r = t;;) {
+                        if (r == root) {
+                            if (base == b &&
+                                U.compareAndSwapObject(a, j, t, null)) {
+                                U.putOrderedInt(this, QBASE, b + 1);
+                                t.doExec();
+                            }
                             return true;
                         }
-                        else
-                            break; // restart
+                        else if ((r = r.completer) == null)
+                            break; // not part of root computation
                     }
-                    if ((r = r.completer) == null)
-                        break outer; // not part of root computation
                 }
             }
             return false;
         }
 
         /**
-         * Executes a top-level task and any local tasks remaining
-         * after execution.
+         * Tries to pop and execute the given task or any other task
+         * in its CountedCompleter computation.
          */
-        final void runTask(ForkJoinTask<?> t) {
-            if (t != null) {
-                (currentSteal = t).doExec();
-                currentSteal = null;
-                ++nsteals;
-                if (base - top < 0) {       // process remaining local tasks
-                    if (mode == 0)
-                        popAndExecAll();
-                    else
-                        pollAndExecAll();
+        final boolean externalPopAndExecCC(CountedCompleter<?> root) {
+            ForkJoinTask<?>[] a; int s; Object o; CountedCompleter<?> t, r;
+            if (base - (s = top) < 0 && (a = array) != null) {
+                long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE;
+                if ((o = U.getObject(a, j)) instanceof CountedCompleter) {
+                    for (t = (CountedCompleter<?>)o, r = t;;) {
+                        if (r == root) {
+                            if (U.compareAndSwapInt(this, QLOCK, 0, 1)) {
+                                if (top == s && array == a &&
+                                    U.compareAndSwapObject(a, j, t, null)) {
+                                    top = s - 1;
+                                    qlock = 0;
+                                    t.doExec();
+                                }
+                                else
+                                    qlock = 0;
+                            }
+                            return true;
+                        }
+                        else if ((r = r.completer) == null)
+                            break;
+                    }
                 }
             }
+            return false;
         }
 
         /**
-         * Executes a non-top-level (stolen) task.
+         * Internal version
          */
-        final void runSubtask(ForkJoinTask<?> t) {
-            if (t != null) {
-                ForkJoinTask<?> ps = currentSteal;
-                (currentSteal = t).doExec();
-                currentSteal = ps;
+        final boolean internalPopAndExecCC(CountedCompleter<?> root) {
+            ForkJoinTask<?>[] a; int s; Object o; CountedCompleter<?> t, r;
+            if (base - (s = top) < 0 && (a = array) != null) {
+                long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE;
+                if ((o = U.getObject(a, j)) instanceof CountedCompleter) {
+                    for (t = (CountedCompleter<?>)o, r = t;;) {
+                        if (r == root) {
+                            if (U.compareAndSwapObject(a, j, t, null)) {
+                                top = s - 1;
+                                t.doExec();
+                            }
+                            return true;
+                        }
+                        else if ((r = r.completer) == null)
+                            break;
+                    }
+                }
             }
+            return false;
         }
 
         /**
@@ -1023,6 +1029,7 @@
 
         // Unsafe mechanics
         private static final sun.misc.Unsafe U;
+        private static final long QBASE;
         private static final long QLOCK;
         private static final int ABASE;
         private static final int ASHIFT;
@@ -1031,6 +1038,8 @@
                 U = sun.misc.Unsafe.getUnsafe();
                 Class<?> k = WorkQueue.class;
                 Class<?> ak = ForkJoinTask[].class;
+                QBASE = U.objectFieldOffset
+                    (k.getDeclaredField("base"));
                 QLOCK = U.objectFieldOffset
                     (k.getDeclaredField("qlock"));
                 ABASE = U.arrayBaseOffset(ak);
@@ -1047,13 +1056,6 @@
     // static fields (initialized in static initializer below)
 
     /**
-     * Creates a new ForkJoinWorkerThread. This factory is used unless
-     * overridden in ForkJoinPool constructors.
-     */
-    public static final ForkJoinWorkerThreadFactory
-        defaultForkJoinWorkerThreadFactory;
-
-    /**
      * Per-thread submission bookkeeping. Shared across all pools
      * to reduce ThreadLocal pollution and because random motion
      * to avoid contention in one pool is likely to hold for others.
@@ -1063,6 +1065,13 @@
     static final ThreadLocal<Submitter> submitters;
 
     /**
+     * Creates a new ForkJoinWorkerThread. This factory is used unless
+     * overridden in ForkJoinPool constructors.
+     */
+    public static final ForkJoinWorkerThreadFactory
+        defaultForkJoinWorkerThreadFactory;
+
+    /**
      * Permission required for callers of methods that may start or
      * kill threads.
      */
@@ -1074,12 +1083,15 @@
      * to paranoically avoid potential initialization circularities
      * as well as to simplify generated code.
      */
-    static final ForkJoinPool commonPool;
+    static final ForkJoinPool common;
 
     /**
-     * Common pool parallelism. Must equal commonPool.parallelism.
+     * Common pool parallelism. To allow simpler use and management
+     * when common pool threads are disabled, we allow the underlying
+     * common.parallelism field to be zero, but in that case still report
+     * parallelism as 1 to reflect resulting caller-runs mechanics.
      */
-    static final int commonPoolParallelism;
+    static final int commonParallelism;
 
     /**
      * Sequence number for creating workerNamePrefix.
@@ -1114,7 +1126,7 @@
     /**
      * Tolerance for idle timeouts, to cope with timer undershoots
      */
-    private static final long TIMEOUT_SLOP = 2000000L; // 20ms
+    private static final long TIMEOUT_SLOP = 2000000L;
 
     /**
      * The maximum stolen->joining link depth allowed in method
@@ -1216,30 +1228,19 @@
     static final int FIFO_QUEUE          =  1;
     static final int SHARED_QUEUE        = -1;
 
-    // bounds for #steps in scan loop -- must be power 2 minus 1
-    private static final int MIN_SCAN    = 0x1ff;   // cover estimation slop
-    private static final int MAX_SCAN    = 0x1ffff; // 4 * max workers
-
-    // Instance fields
-
-    /*
-     * Field layout of this class tends to matter more than one would
-     * like. Runtime layout order is only loosely related to
-     * declaration order and may differ across JVMs, but the following
-     * empirically works OK on current JVMs.
-     */
-
     // Heuristic padding to ameliorate unfortunate memory placements
     volatile long pad00, pad01, pad02, pad03, pad04, pad05, pad06;
 
+    // Instance fields
     volatile long stealCount;                  // collects worker counts
     volatile long ctl;                         // main pool control
     volatile int plock;                        // shutdown status and seqLock
     volatile int indexSeed;                    // worker/submitter index seed
-    final int config;                          // mode and parallelism level
+    final short parallelism;                   // parallelism level
+    final short mode;                          // LIFO/FIFO
     WorkQueue[] workQueues;                    // main registry
     final ForkJoinWorkerThreadFactory factory;
-    final Thread.UncaughtExceptionHandler ueh; // per-worker UEH
+    final UncaughtExceptionHandler ueh;        // per-worker UEH
     final String workerNamePrefix;             // to create worker name string
 
     volatile Object pad10, pad11, pad12, pad13, pad14, pad15, pad16, pad17;
@@ -1254,24 +1255,13 @@
      * a more conservative alternative to a pure spinlock.
      */
     private int acquirePlock() {
-        int spins = PL_SPINS, r = 0, ps, nps;
+        int spins = PL_SPINS, ps, nps;
         for (;;) {
             if (((ps = plock) & PL_LOCK) == 0 &&
                 U.compareAndSwapInt(this, PLOCK, ps, nps = ps + PL_LOCK))
                 return nps;
-            else if (r == 0) { // randomize spins if possible
-                Thread t = Thread.currentThread(); WorkQueue w; Submitter z;
-                if ((t instanceof ForkJoinWorkerThread) &&
-                    (w = ((ForkJoinWorkerThread)t).workQueue) != null)
-                    r = w.seed;
-                else if ((z = submitters.get()) != null)
-                    r = z.seed;
-                else
-                    r = 1;
-            }
             else if (spins >= 0) {
-                r ^= r << 1; r ^= r >>> 3; r ^= r << 10; // xorshift
-                if (r >= 0)
+                if (ThreadLocalRandom.current().nextInt() >= 0)
                     --spins;
             }
             else if (U.compareAndSwapInt(this, PLOCK, ps, ps | PL_SIGNAL)) {
@@ -1303,48 +1293,15 @@
     }
 
     /**
-     * Performs secondary initialization, called when plock is zero.
-     * Creates workQueue array and sets plock to a valid value.  The
-     * lock body must be exception-free (so no try/finally) so we
-     * optimistically allocate new array outside the lock and throw
-     * away if (very rarely) not needed. (A similar tactic is used in
-     * fullExternalPush.)  Because the plock seq value can eventually
-     * wrap around zero, this method harmlessly fails to reinitialize
-     * if workQueues exists, while still advancing plock.
-     *
-     * Additionally tries to create the first worker.
-     */
-    private void initWorkers() {
-        WorkQueue[] ws, nws; int ps;
-        int p = config & SMASK;        // find power of two table size
-        int n = (p > 1) ? p - 1 : 1;   // ensure at least 2 slots
-        n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16;
-        n = (n + 1) << 1;
-        if ((ws = workQueues) == null || ws.length == 0)
-            nws = new WorkQueue[n];
-        else
-            nws = null;
-        if (((ps = plock) & PL_LOCK) != 0 ||
-            !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
-            ps = acquirePlock();
-        if (((ws = workQueues) == null || ws.length == 0) && nws != null)
-            workQueues = nws;
-        int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
-        if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
-            releasePlock(nps);
-        tryAddWorker();
-    }
-
-    /**
      * Tries to create and start one worker if fewer than target
      * parallelism level exist. Adjusts counts etc on failure.
      */
     private void tryAddWorker() {
-        long c; int u;
+        long c; int u, e;
         while ((u = (int)((c = ctl) >>> 32)) < 0 &&
-               (u & SHORT_SIGN) != 0 && (int)c == 0) {
-            long nc = (long)(((u + UTC_UNIT) & UTC_MASK) |
-                             ((u + UAC_UNIT) & UAC_MASK)) << 32;
+               (u & SHORT_SIGN) != 0 && (e = (int)c) >= 0) {
+            long nc = ((long)(((u + UTC_UNIT) & UTC_MASK) |
+                              ((u + UAC_UNIT) & UAC_MASK)) << 32) | (long)e;
             if (U.compareAndSwapLong(this, CTL, c, nc)) {
                 ForkJoinWorkerThreadFactory fac;
                 Throwable ex = null;
@@ -1355,8 +1312,8 @@
                         wt.start();
                         break;
                     }
-                } catch (Throwable e) {
-                    ex = e;
+                } catch (Throwable rex) {
+                    ex = rex;
                 }
                 deregisterWorker(wt, ex);
                 break;
@@ -1377,14 +1334,14 @@
      * @return the worker's queue
      */
     final WorkQueue registerWorker(ForkJoinWorkerThread wt) {
-        Thread.UncaughtExceptionHandler handler; WorkQueue[] ws; int s, ps;
+        UncaughtExceptionHandler handler; WorkQueue[] ws; int s, ps;
         wt.setDaemon(true);
         if ((handler = ueh) != null)
             wt.setUncaughtExceptionHandler(handler);
         do {} while (!U.compareAndSwapInt(this, INDEXSEED, s = indexSeed,
                                           s += SEED_INCREMENT) ||
                      s == 0); // skip 0
-        WorkQueue w = new WorkQueue(this, wt, config >>> 16, s);
+        WorkQueue w = new WorkQueue(this, wt, mode, s);
         if (((ps = plock) & PL_LOCK) != 0 ||
             !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
             ps = acquirePlock();
@@ -1404,14 +1361,15 @@
                         }
                     }
                 }
-                w.eventCount = w.poolIndex = r; // volatile write orders
+                w.poolIndex = (short)r;
+                w.eventCount = r; // volatile write orders
                 ws[r] = w;
             }
         } finally {
             if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
                 releasePlock(nps);
         }
-        wt.setName(workerNamePrefix.concat(Integer.toString(w.poolIndex)));
+        wt.setName(workerNamePrefix.concat(Integer.toString(w.poolIndex >>> 1)));
         return w;
     }
 
@@ -1421,17 +1379,17 @@
      * array, and adjusts counts. If pool is shutting down, tries to
      * complete termination.
      *
-     * @param wt the worker thread or null if construction failed
+     * @param wt the worker thread, or null if construction failed
      * @param ex the exception causing failure, or null if none
      */
     final void deregisterWorker(ForkJoinWorkerThread wt, Throwable ex) {
         WorkQueue w = null;
         if (wt != null && (w = wt.workQueue) != null) {
-            int ps;
+            int ps; long sc;
             w.qlock = -1;                // ensure set
-            long ns = w.nsteals, sc;     // collect steal count
             do {} while (!U.compareAndSwapLong(this, STEALCOUNT,
-                                               sc = stealCount, sc + ns));
+                                               sc = stealCount,
+                                               sc + w.nsteals));
             if (((ps = plock) & PL_LOCK) != 0 ||
                 !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
                 ps = acquirePlock();
@@ -1460,7 +1418,7 @@
                 if (e > 0) {             // activate or create replacement
                     if ((ws = workQueues) == null ||
                         (i = e & SMASK) >= ws.length ||
-                        (v = ws[i]) != null)
+                        (v = ws[i]) == null)
                         break;
                     long nc = (((long)(v.nextWait & E_MASK)) |
                                ((long)(u + UAC_UNIT) << 32));
@@ -1489,6 +1447,26 @@
     // Submissions
 
     /**
+     * Per-thread records for threads that submit to pools. Currently
+     * holds only pseudo-random seed / index that is used to choose
+     * submission queues in method externalPush. In the future, this may
+     * also incorporate a means to implement different task rejection
+     * and resubmission policies.
+     *
+     * Seeds for submitters and workers/workQueues work in basically
+     * the same way but are initialized and updated using slightly
+     * different mechanics. Both are initialized using the same
+     * approach as in class ThreadLocal, where successive values are
+     * unlikely to collide with previous values. Seeds are then
+     * randomly modified upon collisions using xorshifts, which
+     * requires a non-zero seed.
+     */
+    static final class Submitter {
+        int seed;
+        Submitter(int s) { seed = s; }
+    }
+
+    /**
      * Unless shutting down, adds the given task to a submission queue
      * at submitter's current queue index (modulo submission
      * range). Only the most common path is directly handled in this
@@ -1497,19 +1475,21 @@
      * @param task the task. Caller must ensure non-null.
      */
     final void externalPush(ForkJoinTask<?> task) {
-        WorkQueue[] ws; WorkQueue q; Submitter z; int m; ForkJoinTask<?>[] a;
-        if ((z = submitters.get()) != null && plock > 0 &&
-            (ws = workQueues) != null && (m = (ws.length - 1)) >= 0 &&
-            (q = ws[m & z.seed & SQMASK]) != null &&
+        Submitter z = submitters.get();
+        WorkQueue q; int r, m, s, n, am; ForkJoinTask<?>[] a;
+        int ps = plock;
+        WorkQueue[] ws = workQueues;
+        if (z != null && ps > 0 && ws != null && (m = (ws.length - 1)) >= 0 &&
+            (q = ws[m & (r = z.seed) & SQMASK]) != null && r != 0 &&
             U.compareAndSwapInt(q, QLOCK, 0, 1)) { // lock
-            int b = q.base, s = q.top, n, an;
-            if ((a = q.array) != null && (an = a.length) > (n = s + 1 - b)) {
-                int j = (((an - 1) & s) << ASHIFT) + ABASE;
+            if ((a = q.array) != null &&
+                (am = a.length - 1) > (n = (s = q.top) - q.base)) {
+                int j = ((am & s) << ASHIFT) + ABASE;
                 U.putOrderedObject(a, j, task);
                 q.top = s + 1;                     // push on to deque
                 q.qlock = 0;
-                if (n <= 2)
-                    signalWork(q);
+                if (n <= 1)
+                    signalWork(ws, q);
                 return;
             }
             q.qlock = 0;
@@ -1520,13 +1500,19 @@
     /**
      * Full version of externalPush. This method is called, among
      * other times, upon the first submission of the first task to the
-     * pool, so must perform secondary initialization (via
-     * initWorkers). It also detects first submission by an external
-     * thread by looking up its ThreadLocal, and creates a new shared
-     * queue if the one at index if empty or contended. The plock lock
-     * body must be exception-free (so no try/finally) so we
-     * optimistically allocate new queues outside the lock and throw
-     * them away if (very rarely) not needed.
+     * pool, so must perform secondary initialization.  It also
+     * detects first submission by an external thread by looking up
+     * its ThreadLocal, and creates a new shared queue if the one at
+     * index if empty or contended. The plock lock body must be
+     * exception-free (so no try/finally) so we optimistically
+     * allocate new queues outside the lock and throw them away if
+     * (very rarely) not needed.
+     *
+     * Secondary initialization occurs when plock is zero, to create
+     * workQueue array and set plock to a valid value.  This lock body
+     * must also be exception-free. Because the plock seq value can
+     * eventually wrap around zero, this method harmlessly fails to
+     * reinitialize if workQueues exists, while still advancing plock.
      */
     private void fullExternalPush(ForkJoinTask<?> task) {
         int r = 0; // random index seed
@@ -1537,17 +1523,31 @@
                                         r += SEED_INCREMENT) && r != 0)
                     submitters.set(z = new Submitter(r));
             }
-            else if (r == 0) {               // move to a different index
+            else if (r == 0) {                  // move to a different index
                 r = z.seed;
-                r ^= r << 13;                // same xorshift as WorkQueues
+                r ^= r << 13;                   // same xorshift as WorkQueues
                 r ^= r >>> 17;
-                z.seed = r ^ (r << 5);
+                z.seed = r ^= (r << 5);
             }
-            else if ((ps = plock) < 0)
+            if ((ps = plock) < 0)
                 throw new RejectedExecutionException();
             else if (ps == 0 || (ws = workQueues) == null ||
-                     (m = ws.length - 1) < 0)
-                initWorkers();
+                     (m = ws.length - 1) < 0) { // initialize workQueues
+                int p = parallelism;            // find power of two table size
+                int n = (p > 1) ? p - 1 : 1;    // ensure at least 2 slots
+                n |= n >>> 1; n |= n >>> 2;  n |= n >>> 4;
+                n |= n >>> 8; n |= n >>> 16; n = (n + 1) << 1;
+                WorkQueue[] nws = ((ws = workQueues) == null || ws.length == 0 ?
+                                   new WorkQueue[n] : null);
+                if (((ps = plock) & PL_LOCK) != 0 ||
+                    !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+                    ps = acquirePlock();
+                if (((ws = workQueues) == null || ws.length == 0) && nws != null)
+                    workQueues = nws;
+                int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
+                if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+                    releasePlock(nps);
+            }
             else if ((q = ws[k = r & m & SQMASK]) != null) {
                 if (q.qlock == 0 && U.compareAndSwapInt(q, QLOCK, 0, 1)) {
                     ForkJoinTask<?>[] a = q.array;
@@ -1565,7 +1565,7 @@
                         q.qlock = 0;  // unlock
                     }
                     if (submitted) {
-                        signalWork(q);
+                        signalWork(ws, q);
                         return;
                     }
                 }
@@ -1573,6 +1573,7 @@
             }
             else if (((ps = plock) & PL_LOCK) == 0) { // create new queue
                 q = new WorkQueue(this, null, SHARED_QUEUE, r);
+                q.poolIndex = (short)k;
                 if (((ps = plock) & PL_LOCK) != 0 ||
                     !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
                     ps = acquirePlock();
@@ -1583,7 +1584,7 @@
                     releasePlock(nps);
             }
             else
-                r = 0; // try elsewhere while lock held
+                r = 0;
         }
     }
 
@@ -1594,41 +1595,42 @@
      */
     final void incrementActiveCount() {
         long c;
-        do {} while (!U.compareAndSwapLong(this, CTL, c = ctl, c + AC_UNIT));
+        do {} while (!U.compareAndSwapLong
+                     (this, CTL, c = ctl, ((c & ~AC_MASK) |
+                                           ((c & AC_MASK) + AC_UNIT))));
     }
 
     /**
      * Tries to create or activate a worker if too few are active.
      *
-     * @param q the (non-null) queue holding tasks to be signalled
+     * @param ws the worker array to use to find signallees
+     * @param q if non-null, the queue holding tasks to be processed
      */
-    final void signalWork(WorkQueue q) {
-        int hint = q.poolIndex;
-        long c; int e, u, i, n; WorkQueue[] ws; WorkQueue w; Thread p;
-        while ((u = (int)((c = ctl) >>> 32)) < 0) {
-            if ((e = (int)c) > 0) {
-                if ((ws = workQueues) != null && ws.length > (i = e & SMASK) &&
-                    (w = ws[i]) != null && w.eventCount == (e | INT_SIGN)) {
-                    long nc = (((long)(w.nextWait & E_MASK)) |
-                               ((long)(u + UAC_UNIT) << 32));
-                    if (U.compareAndSwapLong(this, CTL, c, nc)) {
-                        w.hint = hint;
-                        w.eventCount = (e + E_SEQ) & E_MASK;
-                        if ((p = w.parker) != null)
-                            U.unpark(p);
-                        break;
-                    }
-                    if (q.top - q.base <= 0)
-                        break;
-                }
-                else
-                    break;
-            }
-            else {
+    final void signalWork(WorkQueue[] ws, WorkQueue q) {
+        for (;;) {
+            long c; int e, u, i; WorkQueue w; Thread p;
+            if ((u = (int)((c = ctl) >>> 32)) >= 0)
+                break;
+            if ((e = (int)c) <= 0) {
                 if ((short)u < 0)
                     tryAddWorker();
                 break;
             }
+            if (ws == null || ws.length <= (i = e & SMASK) ||
+                (w = ws[i]) == null)
+                break;
+            long nc = (((long)(w.nextWait & E_MASK)) |
+                       ((long)(u + UAC_UNIT)) << 32);
+            int ne = (e + E_SEQ) & E_MASK;
+            if (w.eventCount == (e | INT_SIGN) &&
+                U.compareAndSwapLong(this, CTL, c, nc)) {
+                w.eventCount = ne;
+                if ((p = w.parker) != null)
+                    U.unpark(p);
+                break;
+            }
+            if (q != null && q.base >= q.top)
+                break;
         }
     }
 
@@ -1639,214 +1641,154 @@
      */
     final void runWorker(WorkQueue w) {
         w.growArray(); // allocate queue
-        do { w.runTask(scan(w)); } while (w.qlock >= 0);
+        for (int r = w.hint; scan(w, r) == 0; ) {
+            r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift
+        }
     }
 
     /**
-     * Scans for and, if found, returns one task, else possibly
+     * Scans for and, if found, runs one task, else possibly
      * inactivates the worker. This method operates on single reads of
      * volatile state and is designed to be re-invoked continuously,
      * in part because it returns upon detecting inconsistencies,
      * contention, or state changes that indicate possible success on
      * re-invocation.
      *
-     * The scan searches for tasks across queues (starting at a random
-     * index, and relying on registerWorker to irregularly scatter
-     * them within array to avoid bias), checking each at least twice.
-     * The scan terminates upon either finding a non-empty queue, or
-     * completing the sweep. If the worker is not inactivated, it
-     * takes and returns a task from this queue. Otherwise, if not
-     * activated, it signals workers (that may include itself) and
-     * returns so caller can retry. Also returns for true if the
-     * worker array may have changed during an empty scan.  On failure
-     * to find a task, we take one of the following actions, after
-     * which the caller will retry calling this method unless
-     * terminated.
-     *
-     * * If pool is terminating, terminate the worker.
-     *
-     * * If not already enqueued, try to inactivate and enqueue the
-     * worker on wait queue. Or, if inactivating has caused the pool
-     * to be quiescent, relay to idleAwaitWork to possibly shrink
-     * pool.
-     *
-     * * If already enqueued and none of the above apply, possibly
-     * park awaiting signal, else lingering to help scan and signal.
-     *
-     * * If a non-empty queue discovered or left as a hint,
-     * help wake up other workers before return.
+     * The scan searches for tasks across queues starting at a random
+     * index, checking each at least twice.  The scan terminates upon
+     * either finding a non-empty queue, or completing the sweep. If
+     * the worker is not inactivated, it takes and runs a task from
+     * this queue. Otherwise, if not activated, it tries to activate
+     * itself or some other worker by signalling. On failure to find a
+     * task, returns (for retry) if pool state may have changed during
+     * an empty scan, or tries to inactivate if active, else possibly
+     * blocks or terminates via method awaitWork.
      *
      * @param w the worker (via its WorkQueue)
-     * @return a task or null if none found
+     * @param r a random seed
+     * @return worker qlock status if would have waited, else 0
      */
-    private final ForkJoinTask<?> scan(WorkQueue w) {
+    private final int scan(WorkQueue w, int r) {
         WorkQueue[] ws; int m;
-        int ps = plock;                          // read plock before ws
-        if (w != null && (ws = workQueues) != null && (m = ws.length - 1) >= 0) {
-            int ec = w.eventCount;               // ec is negative if inactive
-            int r = w.seed; r ^= r << 13; r ^= r >>> 17; w.seed = r ^= r << 5;
-            w.hint = -1;                         // update seed and clear hint
-            int j = ((m + m + 1) | MIN_SCAN) & MAX_SCAN;
-            do {
-                WorkQueue q; ForkJoinTask<?>[] a; int b;
-                if ((q = ws[(r + j) & m]) != null && (b = q.base) - q.top < 0 &&
-                    (a = q.array) != null) {     // probably nonempty
-                    int i = (((a.length - 1) & b) << ASHIFT) + ABASE;
-                    ForkJoinTask<?> t = (ForkJoinTask<?>)
-                        U.getObjectVolatile(a, i);
-                    if (q.base == b && ec >= 0 && t != null &&
-                        U.compareAndSwapObject(a, i, t, null)) {
-                        if ((q.base = b + 1) - q.top < 0)
-                            signalWork(q);
-                        return t;                // taken
+        long c = ctl;                            // for consistency check
+        if ((ws = workQueues) != null && (m = ws.length - 1) >= 0 && w != null) {
+            for (int j = m + m + 1, ec = w.eventCount;;) {
+                WorkQueue q; int b, e; ForkJoinTask<?>[] a; ForkJoinTask<?> t;
+                if ((q = ws[(r - j) & m]) != null &&
+                    (b = q.base) - q.top < 0 && (a = q.array) != null) {
+                    long i = (((a.length - 1) & b) << ASHIFT) + ABASE;
+                    if ((t = ((ForkJoinTask<?>)
+                              U.getObjectVolatile(a, i))) != null) {
+                        if (ec < 0)
+                            helpRelease(c, ws, w, q, b);
+                        else if (q.base == b &&
+                                 U.compareAndSwapObject(a, i, t, null)) {
+                            U.putOrderedInt(q, QBASE, b + 1);
+                            if ((b + 1) - q.top < 0)
+                                signalWork(ws, q);
+                            w.runTask(t);
+                        }
                     }
-                    else if ((ec < 0 || j < m) && (int)(ctl >> AC_SHIFT) <= 0) {
-                        w.hint = (r + j) & m;    // help signal below
-                        break;                   // cannot take
-                    }
+                    break;
                 }
-            } while (--j >= 0);
-
-            int h, e, ns; long c, sc; WorkQueue q;
-            if ((ns = w.nsteals) != 0) {
-                if (U.compareAndSwapLong(this, STEALCOUNT,
-                                         sc = stealCount, sc + ns))
-                    w.nsteals = 0;               // collect steals and rescan
-            }
-            else if (plock != ps)                // consistency check
-                ;                                // skip
-            else if ((e = (int)(c = ctl)) < 0)
-                w.qlock = -1;                    // pool is terminating
-            else {
-                if ((h = w.hint) < 0) {
-                    if (ec >= 0) {               // try to enqueue/inactivate
-                        long nc = (((long)ec |
-                                    ((c - AC_UNIT) & (AC_MASK|TC_MASK))));
-                        w.nextWait = e;          // link and mark inactive
+                else if (--j < 0) {
+                    if ((ec | (e = (int)c)) < 0) // inactive or terminating
+                        return awaitWork(w, c, ec);
+                    else if (ctl == c) {         // try to inactivate and enqueue
+                        long nc = (long)ec | ((c - AC_UNIT) & (AC_MASK|TC_MASK));
+                        w.nextWait = e;
                         w.eventCount = ec | INT_SIGN;
-                        if (ctl != c || !U.compareAndSwapLong(this, CTL, c, nc))
-                            w.eventCount = ec;   // unmark on CAS failure
-                        else if ((int)(c >> AC_SHIFT) == 1 - (config & SMASK))
-                            idleAwaitWork(w, nc, c);
+                        if (!U.compareAndSwapLong(this, CTL, c, nc))
+                            w.eventCount = ec;   // back out
                     }
-                    else if (w.eventCount < 0 && !tryTerminate(false, false) &&
-                             ctl == c) {         // block
-                        Thread wt = Thread.currentThread();
-                        Thread.interrupted();    // clear status
-                        U.putObject(wt, PARKBLOCKER, this);
-                        w.parker = wt;           // emulate LockSupport.park
-                        if (w.eventCount < 0)    // recheck
-                            U.park(false, 0L);
-                        w.parker = null;
-                        U.putObject(wt, PARKBLOCKER, null);
-                    }
-                }
-                if ((h >= 0 || (h = w.hint) >= 0) &&
-                    (ws = workQueues) != null && h < ws.length &&
-                    (q = ws[h]) != null) {      // signal others before retry
-                    WorkQueue v; Thread p; int u, i, s;
-                    for (int n = (config & SMASK) >>> 1;;) {
-                        int idleCount = (w.eventCount < 0) ? 0 : -1;
-                        if (((s = idleCount - q.base + q.top) <= n &&
-                             (n = s) <= 0) ||
-                            (u = (int)((c = ctl) >>> 32)) >= 0 ||
-                            (e = (int)c) <= 0 || m < (i = e & SMASK) ||
-                            (v = ws[i]) == null)
-                            break;
-                        long nc = (((long)(v.nextWait & E_MASK)) |
-                                   ((long)(u + UAC_UNIT) << 32));
-                        if (v.eventCount != (e | INT_SIGN) ||
-                            !U.compareAndSwapLong(this, CTL, c, nc))
-                            break;
-                        v.hint = h;
-                        v.eventCount = (e + E_SEQ) & E_MASK;
-                        if ((p = v.parker) != null)
-                            U.unpark(p);
-                        if (--n <= 0)
-                            break;
-                    }
+                    break;
                 }
             }
         }
-        return null;
+        return 0;
     }
 
     /**
-     * If inactivating worker w has caused the pool to become
-     * quiescent, checks for pool termination, and, so long as this is
-     * not the only worker, waits for event for up to a given
-     * duration.  On timeout, if ctl has not changed, terminates the
-     * worker, which will in turn wake up another worker to possibly
-     * repeat this process.
+     * A continuation of scan(), possibly blocking or terminating
+     * worker w. Returns without blocking if pool state has apparently
+     * changed since last invocation.  Also, if inactivating w has
+     * caused the pool to become quiescent, checks for pool
+     * termination, and, so long as this is not the only worker, waits
+     * for event for up to a given duration.  On timeout, if ctl has
+     * not changed, terminates the worker, which will in turn wake up
+     * another worker to possibly repeat this process.
      *
      * @param w the calling worker
-     * @param currentCtl the ctl value triggering possible quiescence
-     * @param prevCtl the ctl value to restore if thread is terminated
+     * @param c the ctl value on entry to scan
+     * @param ec the worker's eventCount on entry to scan
      */
-    private void idleAwaitWork(WorkQueue w, long currentCtl, long prevCtl) {
-        if (w != null && w.eventCount < 0 &&
-            !tryTerminate(false, false) && (int)prevCtl != 0) {
-            int dc = -(short)(currentCtl >>> TC_SHIFT);
-            long parkTime = dc < 0 ? FAST_IDLE_TIMEOUT: (dc + 1) * IDLE_TIMEOUT;
-            long deadline = System.nanoTime() + parkTime - TIMEOUT_SLOP;
-            Thread wt = Thread.currentThread();
-            while (ctl == currentCtl) {
-                Thread.interrupted();  // timed variant of version in scan()
-                U.putObject(wt, PARKBLOCKER, this);
-                w.parker = wt;
-                if (ctl == currentCtl)
-                    U.park(false, parkTime);
-                w.parker = null;
-                U.putObject(wt, PARKBLOCKER, null);
-                if (ctl != currentCtl)
-                    break;
-                if (deadline - System.nanoTime() <= 0L &&
-                    U.compareAndSwapLong(this, CTL, currentCtl, prevCtl)) {
-                    w.eventCount = (w.eventCount + E_SEQ) | E_MASK;
-                    w.qlock = -1;   // shrink
-                    break;
+    private final int awaitWork(WorkQueue w, long c, int ec) {
+        int stat, ns; long parkTime, deadline;
+        if ((stat = w.qlock) >= 0 && w.eventCount == ec && ctl == c &&
+            !Thread.interrupted()) {
+            int e = (int)c;
+            int u = (int)(c >>> 32);
+            int d = (u >> UAC_SHIFT) + parallelism; // active count
+
+            if (e < 0 || (d <= 0 && tryTerminate(false, false)))
+                stat = w.qlock = -1;          // pool is terminating
+            else if ((ns = w.nsteals) != 0) { // collect steals and retry
+                long sc;
+                w.nsteals = 0;
+                do {} while (!U.compareAndSwapLong(this, STEALCOUNT,
+                                                   sc = stealCount, sc + ns));
+            }
+            else {
+                long pc = ((d > 0 || ec != (e | INT_SIGN)) ? 0L :
+                           ((long)(w.nextWait & E_MASK)) | // ctl to restore
+                           ((long)(u + UAC_UNIT)) << 32);
+                if (pc != 0L) {               // timed wait if last waiter
+                    int dc = -(short)(c >>> TC_SHIFT);
+                    parkTime = (dc < 0 ? FAST_IDLE_TIMEOUT:
+                                (dc + 1) * IDLE_TIMEOUT);
+                    deadline = System.nanoTime() + parkTime - TIMEOUT_SLOP;
+                }
+                else
+                    parkTime = deadline = 0L;
+                if (w.eventCount == ec && ctl == c) {
+                    Thread wt = Thread.currentThread();
+                    U.putObject(wt, PARKBLOCKER, this);
+                    w.parker = wt;            // emulate LockSupport.park
+                    if (w.eventCount == ec && ctl == c)
+                        U.park(false, parkTime);  // must recheck before park
+                    w.parker = null;
+                    U.putObject(wt, PARKBLOCKER, null);
+                    if (parkTime != 0L && ctl == c &&
+                        deadline - System.nanoTime() <= 0L &&
+                        U.compareAndSwapLong(this, CTL, c, pc))
+                        stat = w.qlock = -1;  // shrink pool
                 }
             }
         }
+        return stat;
     }
 
     /**
-     * Scans through queues looking for work while joining a task; if
-     * any present, signals. May return early if more signalling is
-     * detectably unneeded.
-     *
-     * @param task return early if done
-     * @param origin an index to start scan
+     * Possibly releases (signals) a worker. Called only from scan()
+     * when a worker with apparently inactive status finds a non-empty
+     * queue. This requires revalidating all of the associated state
+     * from caller.
      */
-    private void helpSignal(ForkJoinTask<?> task, int origin) {
-        WorkQueue[] ws; WorkQueue w; Thread p; long c; int m, u, e, i, s;
-        if (task != null && task.status >= 0 &&
-            (u = (int)(ctl >>> 32)) < 0 && (u >> UAC_SHIFT) < 0 &&
-            (ws = workQueues) != null && (m = ws.length - 1) >= 0) {
-            outer: for (int k = origin, j = m; j >= 0; --j) {
-                WorkQueue q = ws[k++ & m];
-                for (int n = m;;) { // limit to at most m signals
-                    if (task.status < 0)
-                        break outer;
-                    if (q == null ||
-                        ((s = -q.base + q.top) <= n && (n = s) <= 0))
-                        break;
-                    if ((u = (int)((c = ctl) >>> 32)) >= 0 ||
-                        (e = (int)c) <= 0 || m < (i = e & SMASK) ||
-                        (w = ws[i]) == null)
-                        break outer;
-                    long nc = (((long)(w.nextWait & E_MASK)) |
-                               ((long)(u + UAC_UNIT) << 32));
-                    if (w.eventCount != (e | INT_SIGN))
-                        break outer;
-                    if (U.compareAndSwapLong(this, CTL, c, nc)) {
-                        w.eventCount = (e + E_SEQ) & E_MASK;
-                        if ((p = w.parker) != null)
-                            U.unpark(p);
-                        if (--n <= 0)
-                            break;
-                    }
-                }
+    private final void helpRelease(long c, WorkQueue[] ws, WorkQueue w,
+                                   WorkQueue q, int b) {
+        WorkQueue v; int e, i; Thread p;
+        if (w != null && w.eventCount < 0 && (e = (int)c) > 0 &&
+            ws != null && ws.length > (i = e & SMASK) &&
+            (v = ws[i]) != null && ctl == c) {
+            long nc = (((long)(v.nextWait & E_MASK)) |
+                       ((long)((int)(c >>> 32) + UAC_UNIT)) << 32);
+            int ne = (e + E_SEQ) & E_MASK;
+            if (q != null && q.base == b && w.eventCount < 0 &&
+                v.eventCount == (e | INT_SIGN) &&
+                U.compareAndSwapLong(this, CTL, c, nc)) {
+                v.eventCount = ne;
+                if ((p = v.parker) != null)
+                    U.unpark(p);
             }
         }
     }
@@ -1871,7 +1813,8 @@
      */
     private int tryHelpStealer(WorkQueue joiner, ForkJoinTask<?> task) {
         int stat = 0, steps = 0;                    // bound to avoid cycles
-        if (joiner != null && task != null) {       // hoist null checks
+        if (task != null && joiner != null &&
+            joiner.base - joiner.top >= 0) {        // hoist checks
             restart: for (;;) {
                 ForkJoinTask<?> subtask = task;     // current target
                 for (WorkQueue j = joiner, v;;) {   // v is stealer of subtask
@@ -1898,7 +1841,7 @@
                         }
                     }
                     for (;;) { // help stealer or descend to its stealer
-                        ForkJoinTask[] a;  int b;
+                        ForkJoinTask[] a; int b;
                         if (subtask.status < 0)     // surround probes with
                             continue restart;       //   consistency checks
                         if ((b = v.base) - v.top < 0 && (a = v.array) != null) {
@@ -1909,13 +1852,23 @@
                                 v.currentSteal != subtask)
                                 continue restart;   // stale
                             stat = 1;               // apparent progress
-                            if (t != null && v.base == b &&
-                                U.compareAndSwapObject(a, i, t, null)) {
-                                v.base = b + 1;     // help stealer
-                                joiner.runSubtask(t);
+                            if (v.base == b) {
+                                if (t == null)
+                                    break restart;
+                                if (U.compareAndSwapObject(a, i, t, null)) {
+                                    U.putOrderedInt(v, QBASE, b + 1);
+                                    ForkJoinTask<?> ps = joiner.currentSteal;
+                                    int jt = joiner.top;
+                                    do {
+                                        joiner.currentSteal = t;
+                                        t.doExec(); // clear local tasks too
+                                    } while (task.status >= 0 &&
+                                             joiner.top != jt &&
+                                             (t = joiner.pop()) != null);
+                                    joiner.currentSteal = ps;
+                                    break restart;
+                                }
                             }
-                            else if (v.base == b && ++steps == MAX_HELP)
-                                break restart;      // v apparently stalled
                         }
                         else {                      // empty -- try to descend
                             ForkJoinTask<?> next = v.currentJoin;
@@ -1942,27 +1895,33 @@
      * and run tasks within the target's computation.
      *
      * @param task the task to join
-     * @param mode if shared, exit upon completing any task
-     * if all workers are active
      */
-    private int helpComplete(ForkJoinTask<?> task, int mode) {
-        WorkQueue[] ws; WorkQueue q; int m, n, s, u;
-        if (task != null && (ws = workQueues) != null &&
-            (m = ws.length - 1) >= 0) {
-            for (int j = 1, origin = j;;) {
+    private int helpComplete(WorkQueue joiner, CountedCompleter<?> task) {
+        WorkQueue[] ws; int m;
+        int s = 0;
+        if ((ws = workQueues) != null && (m = ws.length - 1) >= 0 &&
+            joiner != null && task != null) {
+            int j = joiner.poolIndex;
+            int scans = m + m + 1;
+            long c = 0L;              // for stability check
+            for (int k = scans; ; j += 2) {
+                WorkQueue q;
                 if ((s = task.status) < 0)
-                    return s;
-                if ((q = ws[j & m]) != null && q.pollAndExecCC(task)) {
-                    origin = j;
-                    if (mode == SHARED_QUEUE &&
-                        ((u = (int)(ctl >>> 32)) >= 0 || (u >> UAC_SHIFT) >= 0))
-                        break;
-                }
-                else if ((j = (j + 2) & m) == origin)
                     break;
+                else if (joiner.internalPopAndExecCC(task))
+                    k = scans;
+                else if ((s = task.status) < 0)
+                    break;
+                else if ((q = ws[j & m]) != null && q.pollAndExecCC(task))
+                    k = scans;
+                else if (--k < 0) {
+                    if (c == (c = ctl))
+                        break;
+                    k = scans;
+                }
             }
         }
-        return 0;
+        return s;
     }
 
     /**
@@ -1971,17 +1930,22 @@
      * for blocking. Fails on contention or termination. Otherwise,
      * adds a new thread if no idle workers are available and pool
      * may become starved.
+     *
+     * @param c the assumed ctl value
      */
-    final boolean tryCompensate() {
-        int pc = config & SMASK, e, i, tc; long c;
-        WorkQueue[] ws; WorkQueue w; Thread p;
-        if ((ws = workQueues) != null && (e = (int)(c = ctl)) >= 0) {
-            if (e != 0 && (i = e & SMASK) < ws.length &&
-                (w = ws[i]) != null && w.eventCount == (e | INT_SIGN)) {
+    final boolean tryCompensate(long c) {
+        WorkQueue[] ws = workQueues;
+        int pc = parallelism, e = (int)c, m, tc;
+        if (ws != null && (m = ws.length - 1) >= 0 && e >= 0 && ctl == c) {
+            WorkQueue w = ws[e & m];
+            if (e != 0 && w != null) {
+                Thread p;
                 long nc = ((long)(w.nextWait & E_MASK) |
                            (c & (AC_MASK|TC_MASK)));
-                if (U.compareAndSwapLong(this, CTL, c, nc)) {
-                    w.eventCount = (e + E_SEQ) & E_MASK;
+                int ne = (e + E_SEQ) & E_MASK;
+                if (w.eventCount == (e | INT_SIGN) &&
+                    U.compareAndSwapLong(this, CTL, c, nc)) {
+                    w.eventCount = ne;
                     if ((p = w.parker) != null)
                         U.unpark(p);
                     return true;   // replace with idle worker
@@ -2024,23 +1988,20 @@
      */
     final int awaitJoin(WorkQueue joiner, ForkJoinTask<?> task) {
         int s = 0;
-        if (joiner != null && task != null && (s = task.status) >= 0) {
+        if (task != null && (s = task.status) >= 0 && joiner != null) {
             ForkJoinTask<?> prevJoin = joiner.currentJoin;
             joiner.currentJoin = task;
-            do {} while ((s = task.status) >= 0 && !joiner.isEmpty() &&
-                         joiner.tryRemoveAndExec(task)); // process local tasks
-            if (s >= 0 && (s = task.status) >= 0) {
-                helpSignal(task, joiner.poolIndex);
-                if ((s = task.status) >= 0 &&
-                    (task instanceof CountedCompleter))
-                    s = helpComplete(task, LIFO_QUEUE);
-            }
+            do {} while (joiner.tryRemoveAndExec(task) && // process local tasks
+                         (s = task.status) >= 0);
+            if (s >= 0 && (task instanceof CountedCompleter))
+                s = helpComplete(joiner, (CountedCompleter<?>)task);
+            long cc = 0;        // for stability checks
             while (s >= 0 && (s = task.status) >= 0) {
-                if ((!joiner.isEmpty() ||           // try helping
-                     (s = tryHelpStealer(joiner, task)) == 0) &&
+                if ((s = tryHelpStealer(joiner, task)) == 0 &&
                     (s = task.status) >= 0) {
-                    helpSignal(task, joiner.poolIndex);
-                    if ((s = task.status) >= 0 && tryCompensate()) {
+                    if (!tryCompensate(cc))
+                        cc = ctl;
+                    else {
                         if (task.trySetSignal() && (s = task.status) >= 0) {
                             synchronized (task) {
                                 if (task.status >= 0) {
@@ -2053,9 +2014,11 @@
                                     task.notifyAll();
                             }
                         }
-                        long c;                          // re-activate
+                        long c; // reactivate
                         do {} while (!U.compareAndSwapLong
-                                     (this, CTL, c = ctl, c + AC_UNIT));
+                                     (this, CTL, c = ctl,
+                                      ((c & ~AC_MASK) |
+                                       ((c & AC_MASK) + AC_UNIT))));
                     }
                 }
             }
@@ -2077,15 +2040,11 @@
         if (joiner != null && task != null && (s = task.status) >= 0) {
             ForkJoinTask<?> prevJoin = joiner.currentJoin;
             joiner.currentJoin = task;
-            do {} while ((s = task.status) >= 0 && !joiner.isEmpty() &&
-                         joiner.tryRemoveAndExec(task));
-            if (s >= 0 && (s = task.status) >= 0) {
-                helpSignal(task, joiner.poolIndex);
-                if ((s = task.status) >= 0 &&
-                    (task instanceof CountedCompleter))
-                    s = helpComplete(task, LIFO_QUEUE);
-            }
-            if (s >= 0 && joiner.isEmpty()) {
+            do {} while (joiner.tryRemoveAndExec(task) && // process local tasks
+                         (s = task.status) >= 0);
+            if (s >= 0) {
+                if (task instanceof CountedCompleter)
+                    helpComplete(joiner, (CountedCompleter<?>)task);
                 do {} while (task.status >= 0 &&
                              tryHelpStealer(joiner, task) > 0);
             }
@@ -2095,29 +2054,22 @@
 
     /**
      * Returns a (probably) non-empty steal queue, if one is found
-     * during a random, then cyclic scan, else null.  This method must
-     * be retried by caller if, by the time it tries to use the queue,
-     * it is empty.
-     * @param r a (random) seed for scanning
+     * during a scan, else null.  This method must be retried by
+     * caller if, by the time it tries to use the queue, it is empty.
      */
-    private WorkQueue findNonEmptyStealQueue(int r) {
-        for (WorkQueue[] ws;;) {
-            int ps = plock, m, n;
-            if ((ws = workQueues) == null || (m = ws.length - 1) < 1)
-                return null;
-            for (int j = (m + 1) << 2; ;) {
-                WorkQueue q = ws[(((r + j) << 1) | 1) & m];
-                if (q != null && (n = q.base - q.top) < 0) {
-                    if (n < -1)
-                        signalWork(q);
-                    return q;
-                }
-                else if (--j < 0) {
-                    if (plock == ps)
-                        return null;
-                    break;
+    private WorkQueue findNonEmptyStealQueue() {
+        int r = ThreadLocalRandom.current().nextInt();
+        for (;;) {
+            int ps = plock, m; WorkQueue[] ws; WorkQueue q;
+            if ((ws = workQueues) != null && (m = ws.length - 1) >= 0) {
+                for (int j = (m + 1) << 2; j >= 0; --j) {
+                    if ((q = ws[(((r - j) << 1) | 1) & m]) != null &&
+                        q.base - q.top < 0)
+                        return q;
                 }
             }
+            if (plock == ps)
+                return null;
         }
     }
 
@@ -2128,38 +2080,36 @@
      * find tasks either.
      */
     final void helpQuiescePool(WorkQueue w) {
+        ForkJoinTask<?> ps = w.currentSteal;
         for (boolean active = true;;) {
-            ForkJoinTask<?> localTask; // exhaust local queue
-            while ((localTask = w.nextLocalTask()) != null)
-                localTask.doExec();
-            // Similar to loop in scan(), but ignoring submissions
-            WorkQueue q = findNonEmptyStealQueue(w.nextSeed());
-            if (q != null) {
-                ForkJoinTask<?> t; int b;
+            long c; WorkQueue q; ForkJoinTask<?> t; int b;
+            while ((t = w.nextLocalTask()) != null)
+                t.doExec();
+            if ((q = findNonEmptyStealQueue()) != null) {
                 if (!active) {      // re-establish active count
-                    long c;
                     active = true;
                     do {} while (!U.compareAndSwapLong
-                                 (this, CTL, c = ctl, c + AC_UNIT));
+                                 (this, CTL, c = ctl,
+                                  ((c & ~AC_MASK) |
+                                   ((c & AC_MASK) + AC_UNIT))));
                 }
-                if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null)
-                    w.runSubtask(t);
+                if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null) {
+                    (w.currentSteal = t).doExec();
+                    w.currentSteal = ps;
+                }
             }
-            else {
-                long c;
-                if (active) {       // decrement active count without queuing
+            else if (active) {       // decrement active count without queuing
+                long nc = ((c = ctl) & ~AC_MASK) | ((c & AC_MASK) - AC_UNIT);
+                if ((int)(nc >> AC_SHIFT) + parallelism == 0)
+                    break;          // bypass decrement-then-increment
+                if (U.compareAndSwapLong(this, CTL, c, nc))
                     active = false;
-                    do {} while (!U.compareAndSwapLong
-                                 (this, CTL, c = ctl, c -= AC_UNIT));
-                }
-                else
-                    c = ctl;        // re-increment on exit
-                if ((int)(c >> AC_SHIFT) + (config & SMASK) == 0) {
-                    do {} while (!U.compareAndSwapLong
-                                 (this, CTL, c = ctl, c + AC_UNIT));
-                    break;
-                }
             }
+            else if ((int)((c = ctl) >> AC_SHIFT) + parallelism <= 0 &&
+                     U.compareAndSwapLong
+                     (this, CTL, c, ((c & ~AC_MASK) |
+                                     ((c & AC_MASK) + AC_UNIT))))
+                break;
         }
     }
 
@@ -2173,7 +2123,7 @@
             WorkQueue q; int b;
             if ((t = w.nextLocalTask()) != null)
                 return t;
-            if ((q = findNonEmptyStealQueue(w.nextSeed())) == null)
+            if ((q = findNonEmptyStealQueue()) == null)
                 return null;
             if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null)
                 return t;
@@ -2229,7 +2179,7 @@
     static int getSurplusQueuedTaskCount() {
         Thread t; ForkJoinWorkerThread wt; ForkJoinPool pool; WorkQueue q;
         if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)) {
-            int p = (pool = (wt = (ForkJoinWorkerThread)t).pool).config & SMASK;
+            int p = (pool = (wt = (ForkJoinWorkerThread)t).pool).parallelism;
             int n = (q = wt.workQueue).top - q.base;
             int a = (int)(pool.ctl >> AC_SHIFT) + p;
             return n - (a > (p >>>= 1) ? 0 :
@@ -2258,45 +2208,47 @@
      * @return true if now terminating or terminated
      */
     private boolean tryTerminate(boolean now, boolean enable) {
-        if (this == commonPool)                     // cannot shut down
+        int ps;
+        if (this == common)                        // cannot shut down
             return false;
+        if ((ps = plock) >= 0) {                   // enable by setting plock
+            if (!enable)
+                return false;
+            if ((ps & PL_LOCK) != 0 ||
+                !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+                ps = acquirePlock();
+            int nps = ((ps + PL_LOCK) & ~SHUTDOWN) | SHUTDOWN;
+            if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+                releasePlock(nps);
+        }
         for (long c;;) {
-            if (((c = ctl) & STOP_BIT) != 0) {      // already terminating
-                if ((short)(c >>> TC_SHIFT) == -(config & SMASK)) {
+            if (((c = ctl) & STOP_BIT) != 0) {     // already terminating
+                if ((short)(c >>> TC_SHIFT) + parallelism <= 0) {
                     synchronized (this) {
-                        notifyAll();                // signal when 0 workers
+                        notifyAll();               // signal when 0 workers
                     }
                 }
                 return true;
             }
-            if (plock >= 0) {                       // not yet enabled
-                int ps;
-                if (!enable)
+            if (!now) {                            // check if idle & no tasks
+                WorkQueue[] ws; WorkQueue w;
+                if ((int)(c >> AC_SHIFT) + parallelism > 0)
                     return false;
-                if (((ps = plock) & PL_LOCK) != 0 ||
-                    !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
-                    ps = acquirePlock();
-                if (!U.compareAndSwapInt(this, PLOCK, ps, SHUTDOWN))
-                    releasePlock(SHUTDOWN);
-            }
-            if (!now) {                             // check if idle & no tasks
-                if ((int)(c >> AC_SHIFT) != -(config & SMASK) ||
-                    hasQueuedSubmissions())
-                    return false;
-                // Check for unqueued inactive workers. One pass suffices.
-                WorkQueue[] ws = workQueues; WorkQueue w;
-                if (ws != null) {
-                    for (int i = 1; i < ws.length; i += 2) {
-                        if ((w = ws[i]) != null && w.eventCount >= 0)
+                if ((ws = workQueues) != null) {
+                    for (int i = 0; i < ws.length; ++i) {
+                        if ((w = ws[i]) != null &&
+                            (!w.isEmpty() ||
+                             ((i & 1) != 0 && w.eventCount >= 0))) {
+                            signalWork(ws, w);
                             return false;
+                        }
                     }
                 }
             }
             if (U.compareAndSwapLong(this, CTL, c, c | STOP_BIT)) {
                 for (int pass = 0; pass < 3; ++pass) {
-                    WorkQueue[] ws = workQueues;
-                    if (ws != null) {
-                        WorkQueue w; Thread wt;
+                    WorkQueue[] ws; WorkQueue w; Thread wt;
+                    if ((ws = workQueues) != null) {
                         int n = ws.length;
                         for (int i = 0; i < n; ++i) {
                             if ((w = ws[i]) != null) {
@@ -2307,7 +2259,7 @@
                                         if (!wt.isInterrupted()) {
                                             try {
                                                 wt.interrupt();
-                                            } catch (SecurityException ignore) {
+                                            } catch (Throwable ignore) {
                                             }
                                         }
                                         U.unpark(wt);
@@ -2318,7 +2270,7 @@
                         // Wake up workers parked on event queue
                         int i, e; long cc; Thread p;
                         while ((e = (int)(cc = ctl) & E_MASK) != 0 &&
-                               (i = e & SMASK) < n &&
+                               (i = e & SMASK) < n && i >= 0 &&
                                (w = ws[i]) != null) {
                             long nc = ((long)(w.nextWait & E_MASK) |
                                        ((cc + AC_UNIT) & AC_MASK) |
@@ -2344,9 +2296,9 @@
      * least one task.
      */
     static WorkQueue commonSubmitterQueue() {
-        ForkJoinPool p; WorkQueue[] ws; int m; Submitter z;
+        Submitter z; ForkJoinPool p; WorkQueue[] ws; int m, r;
         return ((z = submitters.get()) != null &&
-                (p = commonPool) != null &&
+                (p = common) != null &&
                 (ws = p.workQueues) != null &&
                 (m = ws.length - 1) >= 0) ?
             ws[m & z.seed & SQMASK] : null;
@@ -2355,127 +2307,57 @@
     /**
      * Tries to pop the given task from submitter's queue in common pool.
      */
-    static boolean tryExternalUnpush(ForkJoinTask<?> t) {
-        ForkJoinPool p; WorkQueue[] ws; WorkQueue q; Submitter z;
-        ForkJoinTask<?>[] a;  int m, s;
-        if (t != null &&
-            (z = submitters.get()) != null &&
-            (p = commonPool) != null &&
-            (ws = p.workQueues) != null &&
-            (m = ws.length - 1) >= 0 &&
-            (q = ws[m & z.seed & SQMASK]) != null &&
-            (s = q.top) != q.base &&
-            (a = q.array) != null) {
+    final boolean tryExternalUnpush(ForkJoinTask<?> task) {
+        WorkQueue joiner; ForkJoinTask<?>[] a; int m, s;
+        Submitter z = submitters.get();
+        WorkQueue[] ws = workQueues;
+        boolean popped = false;
+        if (z != null && ws != null && (m = ws.length - 1) >= 0 &&
+            (joiner = ws[z.seed & m & SQMASK]) != null &&
+            joiner.base != (s = joiner.top) &&
+            (a = joiner.array) != null) {
             long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE;
-            if (U.getObject(a, j) == t &&
-                U.compareAndSwapInt(q, QLOCK, 0, 1)) {
-                if (q.array == a && q.top == s && // recheck
-                    U.compareAndSwapObject(a, j, t, null)) {
-                    q.top = s - 1;
-                    q.qlock = 0;
-                    return true;
+            if (U.getObject(a, j) == task &&
+                U.compareAndSwapInt(joiner, QLOCK, 0, 1)) {
+                if (joiner.top == s && joiner.array == a &&
+                    U.compareAndSwapObject(a, j, task, null)) {
+                    joiner.top = s - 1;
+                    popped = true;
                 }
-                q.qlock = 0;
+                joiner.qlock = 0;
             }
         }
-        return false;
+        return popped;
     }
 
-    /**
-     * Tries to pop and run local tasks within the same computation
-     * as the given root. On failure, tries to help complete from
-     * other queues via helpComplete.
-     */
-    private void externalHelpComplete(WorkQueue q, ForkJoinTask<?> root) {
-        ForkJoinTask<?>[] a; int m;
-        if (q != null && (a = q.array) != null && (m = (a.length - 1)) >= 0 &&
-            root != null && root.status >= 0) {
-            for (;;) {
-                int s, u; Object o; CountedCompleter<?> task = null;
-                if ((s = q.top) - q.base > 0) {
-                    long j = ((m & (s - 1)) << ASHIFT) + ABASE;
-                    if ((o = U.getObject(a, j)) != null &&
-                        (o instanceof CountedCompleter)) {
-                        CountedCompleter<?> t = (CountedCompleter<?>)o, r = t;
-                        do {
-                            if (r == root) {
-                                if (U.compareAndSwapInt(q, QLOCK, 0, 1)) {
-                                    if (q.array == a && q.top == s &&
-                                        U.compareAndSwapObject(a, j, t, null)) {
-                                        q.top = s - 1;
-                                        task = t;
-                                    }
-                                    q.qlock = 0;
-                                }
-                                break;
-                            }
-                        } while ((r = r.completer) != null);
-                    }
-                }
-                if (task != null)
-                    task.doExec();
-                if (root.status < 0 ||
-                    (u = (int)(ctl >>> 32)) >= 0 || (u >> UAC_SHIFT) >= 0)
+    final int externalHelpComplete(CountedCompleter<?> task) {
+        WorkQueue joiner; int m, j;
+        Submitter z = submitters.get();
+        WorkQueue[] ws = workQueues;
+        int s = 0;
+        if (z != null && ws != null && (m = ws.length - 1) >= 0 &&
+            (joiner = ws[(j = z.seed) & m & SQMASK]) != null && task != null) {
+            int scans = m + m + 1;
+            long c = 0L;             // for stability check
+            j |= 1;                  // poll odd queues
+            for (int k = scans; ; j += 2) {
+                WorkQueue q;
+                if ((s = task.status) < 0)
                     break;
-                if (task == null) {
-                    helpSignal(root, q.poolIndex);
-                    if (root.status >= 0)
-                        helpComplete(root, SHARED_QUEUE);
+                else if (joiner.externalPopAndExecCC(task))
+                    k = scans;
+                else if ((s = task.status) < 0)
                     break;
+                else if ((q = ws[j & m]) != null && q.pollAndExecCC(task))
+                    k = scans;
+                else if (--k < 0) {
+                    if (c == (c = ctl))
+                        break;
+                    k = scans;
                 }
             }
         }
-    }
-
-    /**
-     * Tries to help execute or signal availability of the given task
-     * from submitter's queue in common pool.
-     */
-    static void externalHelpJoin(ForkJoinTask<?> t) {
-        // Some hard-to-avoid overlap with tryExternalUnpush
-        ForkJoinPool p; WorkQueue[] ws; WorkQueue q, w; Submitter z;
-        ForkJoinTask<?>[] a;  int m, s, n;
-        if (t != null &&
-            (z = submitters.get()) != null &&
-            (p = commonPool) != null &&
-            (ws = p.workQueues) != null &&
-            (m = ws.length - 1) >= 0 &&
-            (q = ws[m & z.seed & SQMASK]) != null &&
-            (a = q.array) != null) {
-            int am = a.length - 1;
-            if ((s = q.top) != q.base) {
-                long j = ((am & (s - 1)) << ASHIFT) + ABASE;
-                if (U.getObject(a, j) == t &&
-                    U.compareAndSwapInt(q, QLOCK, 0, 1)) {
-                    if (q.array == a && q.top == s &&
-                        U.compareAndSwapObject(a, j, t, null)) {
-                        q.top = s - 1;
-                        q.qlock = 0;
-                        t.doExec();
-                    }
-                    else
-                        q.qlock = 0;
-                }
-            }
-            if (t.status >= 0) {
-                if (t instanceof CountedCompleter)
-                    p.externalHelpComplete(q, t);
-                else
-                    p.helpSignal(t, q.poolIndex);
-            }
-        }
-    }
-
-    /**
-     * Restricted version of helpQuiescePool for external callers
-     */
-    static void externalHelpQuiescePool() {
-        ForkJoinPool p; ForkJoinTask<?> t; WorkQueue q; int b;
-        if ((p = commonPool) != null &&
-            (q = p.findNonEmptyStealQueue(1)) != null &&
-            (b = q.base) - q.top < 0 &&
-            (t = q.pollAt(b)) != null)
-            t.doExec();
+        return s;
     }
 
     // Exported methods
@@ -2529,49 +2411,65 @@
      */
     public ForkJoinPool(int parallelism,
                         ForkJoinWorkerThreadFactory factory,
-                        Thread.UncaughtExceptionHandler handler,
+                        UncaughtExceptionHandler handler,
                         boolean asyncMode) {
+        this(checkParallelism(parallelism),
+             checkFactory(factory),
+             handler,
+             (asyncMode ? FIFO_QUEUE : LIFO_QUEUE),
+             "ForkJoinPool-" + nextPoolId() + "-worker-");
         checkPermission();
-        if (factory == null)
-            throw new NullPointerException();
+    }
+
+    private static int checkParallelism(int parallelism) {
         if (parallelism <= 0 || parallelism > MAX_CAP)
             throw new IllegalArgumentException();
+        return parallelism;
+    }
+
+    private static ForkJoinWorkerThreadFactory checkFactory
+        (ForkJoinWorkerThreadFactory factory) {
+        if (factory == null)
+            throw new NullPointerException();
+        return factory;
+    }
+
+    /**
+     * Creates a {@code ForkJoinPool} with the given parameters, without
+     * any security checks or parameter validation.  Invoked directly by
+     * makeCommonPool.
+     */
+    private ForkJoinPool(int parallelism,
+                         ForkJoinWorkerThreadFactory factory,
+                         UncaughtExceptionHandler handler,
+                         int mode,
+                         String workerNamePrefix) {
+        this.workerNamePrefix = workerNamePrefix;
         this.factory = factory;
         this.ueh = handler;
-        this.config = parallelism | (asyncMode ? (FIFO_QUEUE << 16) : 0);
+        this.mode = (short)mode;
+        this.parallelism = (short)parallelism;
         long np = (long)(-parallelism); // offset ctl counts
         this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
-        int pn = nextPoolId();
-        StringBuilder sb = new StringBuilder("ForkJoinPool-");
-        sb.append(Integer.toString(pn));
-        sb.append("-worker-");
-        this.workerNamePrefix = sb.toString();
     }
 
     /**
-     * Constructor for common pool, suitable only for static initialization.
-     * Basically the same as above, but uses smallest possible initial footprint.
-     */
-    ForkJoinPool(int parallelism, long ctl,
-                 ForkJoinWorkerThreadFactory factory,
-                 Thread.UncaughtExceptionHandler handler) {
-        this.config = parallelism;
-        this.ctl = ctl;
-        this.factory = factory;
-        this.ueh = handler;
-        this.workerNamePrefix = "ForkJoinPool.commonPool-worker-";
-    }
-
-    /**
-     * Returns the common pool instance.
+     * Returns the common pool instance. This pool is statically
+     * constructed; its run state is unaffected by attempts to {@link
+     * #shutdown} or {@link #shutdownNow}. However this pool and any
+     * ongoing processing are automatically terminated upon program
+     * {@link System#exit}.  Any program that relies on asynchronous
+     * task processing to complete before program termination should
+     * invoke {@code commonPool().}{@link #awaitQuiescence awaitQuiescence},
+     * before exit.
      *
      * @return the common pool instance
      * @since 1.8
      * @hide
      */
     public static ForkJoinPool commonPool() {
-        // assert commonPool != null : "static init error";
-        return commonPool;
+        // assert common != null : "static init error";
+        return common;
     }
 
     // Execution methods
@@ -2627,7 +2525,7 @@
         if (task instanceof ForkJoinTask<?>) // avoid re-wrap
             job = (ForkJoinTask<?>) task;
         else
-            job = new ForkJoinTask.AdaptedRunnableAction(task);
+            job = new ForkJoinTask.RunnableExecuteAction(task);
         externalPush(job);
     }
 
@@ -2729,7 +2627,7 @@
      *
      * @return the handler, or {@code null} if none
      */
-    public Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() {
+    public UncaughtExceptionHandler getUncaughtExceptionHandler() {
         return ueh;
     }
 
@@ -2739,7 +2637,8 @@
      * @return the targeted parallelism level of this pool
      */
     public int getParallelism() {
-        return config & SMASK;
+        int par;
+        return ((par = parallelism) > 0) ? par : 1;
     }
 
     /**
@@ -2750,7 +2649,7 @@
      * @hide
      */
     public static int getCommonPoolParallelism() {
-        return commonPoolParallelism;
+        return commonParallelism;
     }
 
     /**
@@ -2762,7 +2661,7 @@
      * @return the number of worker threads
      */
     public int getPoolSize() {
-        return (config & SMASK) + (short)(ctl >>> TC_SHIFT);
+        return parallelism + (short)(ctl >>> TC_SHIFT);
     }
 
     /**
@@ -2772,7 +2671,7 @@
      * @return {@code true} if this pool uses async mode
      */
     public boolean getAsyncMode() {
-        return (config >>> 16) == FIFO_QUEUE;
+        return mode == FIFO_QUEUE;
     }
 
     /**
@@ -2803,7 +2702,7 @@
      * @return the number of active threads
      */
     public int getActiveThreadCount() {
-        int r = (config & SMASK) + (int)(ctl >> AC_SHIFT);
+        int r = parallelism + (int)(ctl >> AC_SHIFT);
         return (r <= 0) ? 0 : r; // suppress momentarily negative values
     }
 
@@ -2819,7 +2718,7 @@
      * @return {@code true} if all threads are currently idle
      */
     public boolean isQuiescent() {
-        return (int)(ctl >> AC_SHIFT) + (config & SMASK) == 0;
+        return parallelism + (int)(ctl >> AC_SHIFT) <= 0;
     }
 
     /**
@@ -2982,7 +2881,7 @@
                 }
             }
         }
-        int pc = (config & SMASK);
+        int pc = parallelism;
         int tc = pc + (short)(c >>> TC_SHIFT);
         int ac = pc + (int)(c >> AC_SHIFT);
         if (ac < 0) // ignore transient negative
@@ -3012,11 +2911,6 @@
      * already shut down.  Tasks that are in the process of being
      * submitted concurrently during the course of this method may or
      * may not be rejected.
-     *
-     * @throws SecurityException if a security manager exists and
-     *         the caller is not permitted to modify threads
-     *         because it does not hold {@link
-     *         java.lang.RuntimePermission}{@code ("modifyThread")}
      */
     public void shutdown() {
         checkPermission();
@@ -3051,7 +2945,7 @@
     public boolean isTerminated() {
         long c = ctl;
         return ((c & STOP_BIT) != 0L &&
-                (short)(c >>> TC_SHIFT) == -(config & SMASK));
+                (short)(c >>> TC_SHIFT) + parallelism <= 0);
     }
 
     /**
@@ -3070,7 +2964,7 @@
     public boolean isTerminating() {
         long c = ctl;
         return ((c & STOP_BIT) != 0L &&
-                (short)(c >>> TC_SHIFT) != -(config & SMASK));
+                (short)(c >>> TC_SHIFT) + parallelism > 0);
     }
 
     /**
@@ -3085,9 +2979,10 @@
     /**
      * Blocks until all tasks have completed execution after a
      * shutdown request, or the timeout occurs, or the current thread
-     * is interrupted, whichever happens first. Note that the {@link
-     * #commonPool()} never terminates until program shutdown so
-     * this method will always time out.
+     * is interrupted, whichever happens first. Because the {@link
+     * #commonPool()} never terminates until program shutdown, when
+     * applied to the common pool, this method is equivalent to {@link
+     * #awaitQuiescence(long, TimeUnit)} but always returns {@code false}.
      *
      * @param timeout the maximum time to wait
      * @param unit the time unit of the timeout argument
@@ -3097,6 +2992,12 @@
      */
     public boolean awaitTermination(long timeout, TimeUnit unit)
         throws InterruptedException {
+        if (Thread.interrupted())
+            throw new InterruptedException();
+        if (this == common) {
+            awaitQuiescence(timeout, unit);
+            return false;
+        }
         long nanos = unit.toNanos(timeout);
         if (isTerminated())
             return true;
@@ -3117,6 +3018,59 @@
     }
 
     /**
+     * If called by a ForkJoinTask operating in this pool, equivalent
+     * in effect to {@link ForkJoinTask#helpQuiesce}. Otherwise,
+     * waits and/or attempts to assist performing tasks until this
+     * pool {@link #isQuiescent} or the indicated timeout elapses.
+     *
+     * @param timeout the maximum time to wait
+     * @param unit the time unit of the timeout argument
+     * @return {@code true} if quiescent; {@code false} if the
+     * timeout elapsed.
+     */
+    public boolean awaitQuiescence(long timeout, TimeUnit unit) {
+        long nanos = unit.toNanos(timeout);
+        ForkJoinWorkerThread wt;
+        Thread thread = Thread.currentThread();
+        if ((thread instanceof ForkJoinWorkerThread) &&
+            (wt = (ForkJoinWorkerThread)thread).pool == this) {
+            helpQuiescePool(wt.workQueue);
+            return true;
+        }
+        long startTime = System.nanoTime();
+        WorkQueue[] ws;
+        int r = 0, m;
+        boolean found = true;
+        while (!isQuiescent() && (ws = workQueues) != null &&
+               (m = ws.length - 1) >= 0) {
+            if (!found) {
+                if ((System.nanoTime() - startTime) > nanos)
+                    return false;
+                Thread.yield(); // cannot block
+            }
+            found = false;
+            for (int j = (m + 1) << 2; j >= 0; --j) {
+                ForkJoinTask<?> t; WorkQueue q; int b;
+                if ((q = ws[r++ & m]) != null && (b = q.base) - q.top < 0) {
+                    found = true;
+                    if ((t = q.pollAt(b)) != null)
+                        t.doExec();
+                    break;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Waits and/or attempts to assist performing tasks indefinitely
+     * until the {@link #commonPool()} {@link #isQuiescent}.
+     */
+    static void quiesceCommonPool() {
+        common.awaitQuiescence(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
+    }
+
+    /**
      * Interface for extending managed parallelism for tasks running
      * in {@link ForkJoinPool}s.
      *
@@ -3125,9 +3079,9 @@
      * not necessary. Method {@code block} blocks the current thread
      * if necessary (perhaps internally invoking {@code isReleasable}
      * before actually blocking). These actions are performed by any
-     * thread invoking {@link ForkJoinPool#managedBlock}.  The
-     * unusual methods in this API accommodate synchronizers that may,
-     * but don't usually, block for long periods. Similarly, they
+     * thread invoking {@link ForkJoinPool#managedBlock(ManagedBlocker)}.
+     * The unusual methods in this API accommodate synchronizers that
+     * may, but don't usually, block for long periods. Similarly, they
      * allow more efficient internal handling of cases in which
      * additional workers may be, but usually are not, needed to
      * ensure sufficient parallelism.  Toward this end,
@@ -3185,6 +3139,7 @@
 
         /**
          * Returns {@code true} if blocking is unnecessary.
+         * @return {@code true} if blocking is unnecessary
          */
         boolean isReleasable();
     }
@@ -3214,21 +3169,8 @@
         Thread t = Thread.currentThread();
         if (t instanceof ForkJoinWorkerThread) {
             ForkJoinPool p = ((ForkJoinWorkerThread)t).pool;
-            while (!blocker.isReleasable()) { // variant of helpSignal
-                WorkQueue[] ws; WorkQueue q; int m, u;
-                if ((ws = p.workQueues) != null && (m = ws.length - 1) >= 0) {
-                    for (int i = 0; i <= m; ++i) {
-                        if (blocker.isReleasable())
-                            return;
-                        if ((q = ws[i]) != null && q.base - q.top < 0) {
-                            p.signalWork(q);
-                            if ((u = (int)(p.ctl >>> 32)) >= 0 ||
-                                (u >> UAC_SHIFT) >= 0)
-                                break;
-                        }
-                    }
-                }
-                if (p.tryCompensate()) {
+            while (!blocker.isReleasable()) {
+                if (p.tryCompensate(p.ctl)) {
                     try {
                         do {} while (!blocker.isReleasable() &&
                                      !blocker.block());
@@ -3266,6 +3208,7 @@
     private static final long STEALCOUNT;
     private static final long PLOCK;
     private static final long INDEXSEED;
+    private static final long QBASE;
     private static final long QLOCK;
 
     static {
@@ -3285,6 +3228,8 @@
             PARKBLOCKER = U.objectFieldOffset
                 (tk.getDeclaredField("parkBlocker"));
             Class<?> wk = WorkQueue.class;
+            QBASE = U.objectFieldOffset
+                (wk.getDeclaredField("base"));
             QLOCK = U.objectFieldOffset
                 (wk.getDeclaredField("qlock"));
             Class<?> ak = ForkJoinTask[].class;
@@ -3298,45 +3243,51 @@
         }
 
         submitters = new ThreadLocal<Submitter>();
-        ForkJoinWorkerThreadFactory fac = defaultForkJoinWorkerThreadFactory =
+        defaultForkJoinWorkerThreadFactory =
             new DefaultForkJoinWorkerThreadFactory();
         modifyThreadPermission = new RuntimePermission("modifyThread");
 
-        /*
-         * Establish common pool parameters.  For extra caution,
-         * computations to set up common pool state are here; the
-         * constructor just assigns these values to fields.
-         */
+        common = java.security.AccessController.doPrivileged
+            (new java.security.PrivilegedAction<ForkJoinPool>() {
+                public ForkJoinPool run() { return makeCommonPool(); }});
+        int par = common.parallelism; // report 1 even if threads disabled
+        commonParallelism = par > 0 ? par : 1;
+    }
 
-        int par = 0;
-        Thread.UncaughtExceptionHandler handler = null;
-        try {  // TBD: limit or report ignored exceptions?
+    /**
+     * Creates and returns the common pool, respecting user settings
+     * specified via system properties.
+     */
+    private static ForkJoinPool makeCommonPool() {
+        int parallelism = -1;
+        ForkJoinWorkerThreadFactory factory
+            = defaultForkJoinWorkerThreadFactory;
+        UncaughtExceptionHandler handler = null;
+        try {  // ignore exceptions in accessing/parsing properties
             String pp = System.getProperty
                 ("java.util.concurrent.ForkJoinPool.common.parallelism");
-            String hp = System.getProperty
-                ("java.util.concurrent.ForkJoinPool.common.exceptionHandler");
             String fp = System.getProperty
                 ("java.util.concurrent.ForkJoinPool.common.threadFactory");
-            if (fp != null)
-                fac = ((ForkJoinWorkerThreadFactory)ClassLoader.
-                       getSystemClassLoader().loadClass(fp).newInstance());
-            if (hp != null)
-                handler = ((Thread.UncaughtExceptionHandler)ClassLoader.
-                           getSystemClassLoader().loadClass(hp).newInstance());
+            String hp = System.getProperty
+                ("java.util.concurrent.ForkJoinPool.common.exceptionHandler");
             if (pp != null)
-                par = Integer.parseInt(pp);
+                parallelism = Integer.parseInt(pp);
+            if (fp != null)
+                factory = ((ForkJoinWorkerThreadFactory)ClassLoader.
+                           getSystemClassLoader().loadClass(fp).newInstance());
+            if (hp != null)
+                handler = ((UncaughtExceptionHandler)ClassLoader.
+                           getSystemClassLoader().loadClass(hp).newInstance());
         } catch (Exception ignore) {
         }
 
-        if (par <= 0)
-            par = Runtime.getRuntime().availableProcessors();
-        if (par > MAX_CAP)
-            par = MAX_CAP;
-        commonPoolParallelism = par;
-        long np = (long)(-par); // precompute initial ctl value
-        long ct = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
-
-        commonPool = new ForkJoinPool(par, ct, fac, handler);
+        if (parallelism < 0 && // default 1 less than #cores
+            (parallelism = Runtime.getRuntime().availableProcessors() - 1) < 0)
+            parallelism = 0;
+        if (parallelism > MAX_CAP)
+            parallelism = MAX_CAP;
+        return new ForkJoinPool(parallelism, factory, handler, LIFO_QUEUE,
+                                "ForkJoinPool.commonPool-worker-");
     }
 
 }
diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinTask.java b/luni/src/main/java/java/util/concurrent/ForkJoinTask.java
index 818788e..6d25775 100644
--- a/luni/src/main/java/java/util/concurrent/ForkJoinTask.java
+++ b/luni/src/main/java/java/util/concurrent/ForkJoinTask.java
@@ -136,7 +136,7 @@
  * supports other methods and techniques (for example the use of
  * {@link Phaser}, {@link #helpQuiesce}, and {@link #complete}) that
  * may be of use in constructing custom subclasses for problems that
- * are not statically structured as DAGs. To support such usages a
+ * are not statically structured as DAGs. To support such usages, a
  * ForkJoinTask may be atomically <em>tagged</em> with a {@code short}
  * value using {@link #setForkJoinTaskTag} or {@link
  * #compareAndSetForkJoinTaskTag} and checked using {@link
@@ -286,25 +286,35 @@
      */
     private int externalAwaitDone() {
         int s;
-        ForkJoinPool.externalHelpJoin(this);
-        boolean interrupted = false;
-        while ((s = status) >= 0) {
-            if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
-                synchronized (this) {
-                    if (status >= 0) {
-                        try {
-                            wait();
-                        } catch (InterruptedException ie) {
-                            interrupted = true;
+        ForkJoinPool cp = ForkJoinPool.common;
+        if ((s = status) >= 0) {
+            if (cp != null) {
+                if (this instanceof CountedCompleter)
+                    s = cp.externalHelpComplete((CountedCompleter<?>)this);
+                else if (cp.tryExternalUnpush(this))
+                    s = doExec();
+            }
+            if (s >= 0 && (s = status) >= 0) {
+                boolean interrupted = false;
+                do {
+                    if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+                        synchronized (this) {
+                            if (status >= 0) {
+                                try {
+                                    wait();
+                                } catch (InterruptedException ie) {
+                                    interrupted = true;
+                                }
+                            }
+                            else
+                                notifyAll();
                         }
                     }
-                    else
-                        notifyAll();
-                }
+                } while ((s = status) >= 0);
+                if (interrupted)
+                    Thread.currentThread().interrupt();
             }
         }
-        if (interrupted)
-            Thread.currentThread().interrupt();
         return s;
     }
 
@@ -313,9 +323,15 @@
      */
     private int externalInterruptibleAwaitDone() throws InterruptedException {
         int s;
+        ForkJoinPool cp = ForkJoinPool.common;
         if (Thread.interrupted())
             throw new InterruptedException();
-        ForkJoinPool.externalHelpJoin(this);
+        if ((s = status) >= 0 && cp != null) {
+            if (this instanceof CountedCompleter)
+                cp.externalHelpComplete((CountedCompleter<?>)this);
+            else if (cp.tryExternalUnpush(this))
+                doExec();
+        }
         while ((s = status) >= 0) {
             if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
                 synchronized (this) {
@@ -329,7 +345,6 @@
         return s;
     }
 
-
     /**
      * Implementation for join, get, quietlyJoin. Directly handles
      * only cases of already-completed, external wait, and
@@ -601,14 +616,9 @@
     /**
      * A version of "sneaky throw" to relay exceptions
      */
-    static void rethrow(final Throwable ex) {
-        if (ex != null) {
-            if (ex instanceof Error)
-                throw (Error)ex;
-            if (ex instanceof RuntimeException)
-                throw (RuntimeException)ex;
-            throw uncheckedThrowable(ex, RuntimeException.class);
-        }
+    static void rethrow(Throwable ex) {
+        if (ex != null)
+            ForkJoinTask.<RuntimeException>uncheckedThrow(ex);
     }
 
     /**
@@ -617,8 +627,8 @@
      * unchecked exceptions
      */
     @SuppressWarnings("unchecked") static <T extends Throwable>
-        T uncheckedThrowable(final Throwable t, final Class<T> c) {
-        return (T)t; // rely on vacuous cast
+        void uncheckedThrow(Throwable t) throws T {
+        throw (T)t; // rely on vacuous cast
     }
 
     /**
@@ -653,7 +663,7 @@
         if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
             ((ForkJoinWorkerThread)t).workQueue.push(this);
         else
-            ForkJoinPool.commonPool.externalPush(this);
+            ForkJoinPool.common.externalPush(this);
         return this;
     }
 
@@ -774,8 +784,6 @@
      * @param tasks the collection of tasks
      * @return the tasks argument, to simplify usage
      * @throws NullPointerException if tasks or any element are null
-
-     * @hide
      */
     public static <T extends ForkJoinTask<?>> Collection<T> invokeAll(Collection<T> tasks) {
         if (!(tasks instanceof RandomAccess) || !(tasks instanceof List<?>)) {
@@ -831,7 +839,7 @@
      * <p>This method is designed to be invoked by <em>other</em>
      * tasks. To terminate the current task, you can just return or
      * throw an unchecked exception from its computation method, or
-     * invoke {@link #completeExceptionally}.
+     * invoke {@link #completeExceptionally(Throwable)}.
      *
      * @param mayInterruptIfRunning this value has no effect in the
      * default implementation because interrupts are not used to
@@ -984,6 +992,7 @@
         // Messy in part because we measure in nanosecs, but wait in millisecs
         int s; long ms;
         long ns = unit.toNanos(timeout);
+        ForkJoinPool cp;
         if ((s = status) >= 0 && ns > 0L) {
             long deadline = System.nanoTime() + ns;
             ForkJoinPool p = null;
@@ -995,8 +1004,12 @@
                 w = wt.workQueue;
                 p.helpJoinOnce(w, this); // no retries on failure
             }
-            else
-                ForkJoinPool.externalHelpJoin(this);
+            else if ((cp = ForkJoinPool.common) != null) {
+                if (this instanceof CountedCompleter)
+                    cp.externalHelpComplete((CountedCompleter<?>)this);
+                else if (cp.tryExternalUnpush(this))
+                    doExec();
+            }
             boolean canBlock = false;
             boolean interrupted = false;
             try {
@@ -1004,7 +1017,7 @@
                     if (w != null && w.qlock < 0)
                         cancelIgnoringExceptions(this);
                     else if (!canBlock) {
-                        if (p == null || p.tryCompensate())
+                        if (p == null || p.tryCompensate(p.ctl))
                             canBlock = true;
                     }
                     else {
@@ -1080,7 +1093,7 @@
             wt.pool.helpQuiescePool(wt.workQueue);
         }
         else
-            ForkJoinPool.externalHelpQuiescePool();
+            ForkJoinPool.quiesceCommonPool();
     }
 
     /**
@@ -1145,7 +1158,7 @@
         Thread t;
         return (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
                 ((ForkJoinWorkerThread)t).workQueue.tryUnpush(this) :
-                ForkJoinPool.tryExternalUnpush(this));
+                ForkJoinPool.common.tryExternalUnpush(this));
     }
 
     /**
@@ -1316,7 +1329,7 @@
      *
      * @param e the expected tag value
      * @param tag the new tag value
-     * @return true if successful; i.e., the current value was
+     * @return {@code true} if successful; i.e., the current value was
      * equal to e and is now tag.
      * @since 1.8
      * @hide
@@ -1370,6 +1383,24 @@
     }
 
     /**
+     * Adaptor for Runnables in which failure forces worker exception
+     */
+    static final class RunnableExecuteAction extends ForkJoinTask<Void> {
+        final Runnable runnable;
+        RunnableExecuteAction(Runnable runnable) {
+            if (runnable == null) throw new NullPointerException();
+            this.runnable = runnable;
+        }
+        public final Void getRawResult() { return null; }
+        public final void setRawResult(Void v) { }
+        public final boolean exec() { runnable.run(); return true; }
+        void internalPropagateException(Throwable ex) {
+            rethrow(ex); // rethrow outside exec() catches.
+        }
+        private static final long serialVersionUID = 5232453952276885070L;
+    }
+
+    /**
      * Adaptor for Callables
      */
     static final class AdaptedCallable<T> extends ForkJoinTask<T>
@@ -1480,5 +1511,4 @@
             throw new Error(e);
         }
     }
-
 }
diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java b/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java
index f31763c..5f2799b 100644
--- a/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java
+++ b/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java
@@ -14,8 +14,8 @@
  * scheduling or execution.  However, you can override initialization
  * and termination methods surrounding the main task processing loop.
  * If you do create such a subclass, you will also need to supply a
- * custom {@link ForkJoinPool.ForkJoinWorkerThreadFactory} to use it
- * in a {@code ForkJoinPool}.
+ * custom {@link ForkJoinPool.ForkJoinWorkerThreadFactory} to
+ * {@linkplain ForkJoinPool#ForkJoinPool use it} in a {@code ForkJoinPool}.
  *
  * @since 1.7
  * @hide
@@ -61,16 +61,17 @@
     }
 
     /**
-     * Returns the index number of this thread in its pool.  The
-     * returned value ranges from zero to the maximum number of
-     * threads (minus one) that have ever been created in the pool.
-     * This method may be useful for applications that track status or
-     * collect results per-worker rather than per-task.
+     * Returns the unique index number of this thread in its pool.
+     * The returned value ranges from zero to the maximum number of
+     * threads (minus one) that may exist in the pool, and does not
+     * change during the lifetime of the thread.  This method may be
+     * useful for applications that track status or collect results
+     * per-worker-thread rather than per-task.
      *
      * @return the index number
      */
     public int getPoolIndex() {
-        return workQueue.poolIndex;
+        return workQueue.poolIndex >>> 1; // ignore odd/even tag bit
     }
 
     /**
diff --git a/luni/src/main/java/java/util/spi/CurrencyNameProvider.java b/luni/src/main/java/java/util/spi/CurrencyNameProvider.java
deleted file mode 100644
index d717aa2..0000000
--- a/luni/src/main/java/java/util/spi/CurrencyNameProvider.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*

- *  Licensed to the Apache Software Foundation (ASF) under one or more

- *  contributor license agreements.  See the NOTICE file distributed with

- *  this work for additional information regarding copyright ownership.

- *  The ASF licenses this file to You 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 java.util.spi;

-

-import java.util.Locale;

-

-/**

- * This abstract class should be extended by service providers that provide

- * localized currency symbols (currency names) from currency codes.

- * <p>Note that Android does not support user-supplied locale service providers.

- * @since 1.6

- * @hide

- */

-public abstract class CurrencyNameProvider extends LocaleServiceProvider {

-    /**

-     * Default constructor, for use by subclasses.

-     */

-    protected CurrencyNameProvider() {

-        // do nothing

-    }

-

-    /**

-     * Returns the localized currency symbol for the given currency code.

-     *

-     * @param code an ISO 4217 currency code

-     * @param locale a locale

-     * @return the symbol or null if there is no available symbol in the locale

-     * @throws NullPointerException

-     *             if {@code code == null || locale == null}

-     * @throws IllegalArgumentException

-     *             if code or locale is not in a legal format or not available

-     */

-    public abstract String getSymbol(String code, Locale locale);

-}

diff --git a/luni/src/main/java/java/util/spi/LocaleNameProvider.java b/luni/src/main/java/java/util/spi/LocaleNameProvider.java
deleted file mode 100644
index 0d25074..0000000
--- a/luni/src/main/java/java/util/spi/LocaleNameProvider.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*

- *  Licensed to the Apache Software Foundation (ASF) under one or more

- *  contributor license agreements.  See the NOTICE file distributed with

- *  this work for additional information regarding copyright ownership.

- *  The ASF licenses this file to You 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 java.util.spi;

-

-import java.util.Locale;

-

-/**

- * This abstract class should be extended by service providers that provide

- * localized locale names.

- * <p>Note that Android does not support user-supplied locale service providers.

- * @since 1.6

- * @hide

- */

-public abstract class LocaleNameProvider extends LocaleServiceProvider {

-    /**

-     * Default constructor, for use by subclasses.

-     */

-    protected LocaleNameProvider() {

-        // do nothing

-    }

-

-    /**

-     * Returns the localized name for the given ISO 639 language code.

-     *

-     * @param languageCode an ISO 639 language code

-     * @param locale a locale

-     * @return the name or null if unavailable

-     * @throws NullPointerException

-     *             if {@code code == null || locale == null}

-     * @throws IllegalArgumentException

-     *             if code or locale is not in a legal format or not available

-     */

-    public abstract String getDisplayLanguage(String languageCode, Locale locale);

-

-    /**

-     * Returns the localized name for the given ISO 3166 country code.

-     *

-     * @param countryCode an ISO 3166 language code

-     * @param locale a locale

-     * @return the name or null if unavailable

-     * @throws NullPointerException

-     *             if {@code code == null || locale == null}

-     * @throws IllegalArgumentException

-     *             if code or locale is not in a legal format or not available

-     */

-    public abstract String getDisplayCountry(String countryCode, Locale locale);

-

-    /**

-     * Returns the localized name for the given variant code.

-     *

-     * @param variantCode a variant code

-     * @param locale a locale

-     * @return the name or null if unavailable

-     * @throws NullPointerException

-     *             if {@code code == null || locale == null}

-     * @throws IllegalArgumentException

-     *             if code or locale is not in a legal format or not available

-     */

-    public abstract String getDisplayVariant(String variantCode, Locale locale);

-}

diff --git a/luni/src/main/java/java/util/spi/LocaleServiceProvider.java b/luni/src/main/java/java/util/spi/LocaleServiceProvider.java
deleted file mode 100644
index b1b62de..0000000
--- a/luni/src/main/java/java/util/spi/LocaleServiceProvider.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*

- *  Licensed to the Apache Software Foundation (ASF) under one or more

- *  contributor license agreements.  See the NOTICE file distributed with

- *  this work for additional information regarding copyright ownership.

- *  The ASF licenses this file to You 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 java.util.spi;

-

-import java.util.Locale;

-

-/**

- * The base class for all the locale related service provider interfaces (SPIs).

- * <p>Note that Android does not support user-supplied locale service providers.

- * @since 1.6

- * @hide

- */

-public abstract class LocaleServiceProvider {

-    /**

-     * Default constructor, for use by subclasses.

-     */

-    protected LocaleServiceProvider() {

-        // do nothing

-    }

-

-    /**

-     * Returns all locales for which this locale service provider has localized objects or names.

-     */

-    public abstract Locale[] getAvailableLocales();

-}

diff --git a/luni/src/main/java/java/util/spi/TimeZoneNameProvider.java b/luni/src/main/java/java/util/spi/TimeZoneNameProvider.java
deleted file mode 100644
index 533d14e..0000000
--- a/luni/src/main/java/java/util/spi/TimeZoneNameProvider.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*

- *  Licensed to the Apache Software Foundation (ASF) under one or more

- *  contributor license agreements.  See the NOTICE file distributed with

- *  this work for additional information regarding copyright ownership.

- *  The ASF licenses this file to You 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 java.util.spi;

-

-import java.util.Locale;

-

-/**

- * This abstract class should be extended by service providers that provide

- * localized time zone names.

- * <p>Note that Android does not support user-supplied locale service providers.

- * @since 1.6

- * @hide

- */

-public abstract class TimeZoneNameProvider extends LocaleServiceProvider {

-    /**

-     * Default constructor, for use by subclasses.

-     */

-    protected TimeZoneNameProvider() {

-        // do nothing

-    }

-

-    /**

-     * Returns the localized name for the given time zone in the given locale.

-     *

-     * @param id the time zone id

-     * @param daylight true to return the name for daylight saving time.

-     * @param style TimeZone.LONG or TimeZone.SHORT

-     * @param locale the locale

-     * @return the readable time zone name, or null if it is unavailable

-     * @throws NullPointerException

-     *             if {@code id == null || locale == null}

-     * @throws IllegalArgumentException

-     *             if locale is not available or style is invalid

-     */

-    public abstract String getDisplayName(String id, boolean daylight, int style, Locale locale);

-}

diff --git a/luni/src/main/java/org/apache/harmony/security/fortress/Services.java b/luni/src/main/java/org/apache/harmony/security/fortress/Services.java
index 4fe0d44..0e70880 100644
--- a/luni/src/main/java/org/apache/harmony/security/fortress/Services.java
+++ b/luni/src/main/java/org/apache/harmony/security/fortress/Services.java
@@ -158,7 +158,7 @@
     }
 
     /**
-     * Returns true if services contain any provider information.
+     * Returns true if services does not contain any provider information.
      */
     public static synchronized boolean isEmpty() {
         return services.isEmpty();