changeset 6406:30db89b2808e

Add slice op (combination of skip+limit); merge SkipOP and LimitOp implementations into a single SliceOp; parallel implementation of SliceOp; more tests
author briangoetz
date Thu, 15 Nov 2012 00:47:35 -0500
parents abcf56edb55c
children 5a54b0ddbbcb
files make/com/sun/crypto/provider/Makefile make/com/sun/net/httpserver/Makefile make/docs/CORE_PKGS.gmk make/sun/security/other/Makefile makefiles/docs/CORE_PKGS.gmk src/share/classes/java/util/Arrays.java src/share/classes/java/util/concurrent/CountedCompleter.java src/share/classes/java/util/concurrent/ForkJoinPool.java src/share/classes/java/util/concurrent/ForkJoinTask.java src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java src/share/classes/java/util/streams/ReferencePipeline.java src/share/classes/java/util/streams/Sink.java src/share/classes/java/util/streams/Stream.java src/share/classes/java/util/streams/StreamShapeFactory.java src/share/classes/java/util/streams/ops/AbstractTask.java src/share/classes/java/util/streams/ops/ConcatOp.java src/share/classes/java/util/streams/ops/CumulateOp.java src/share/classes/java/util/streams/ops/FilterOp.java src/share/classes/java/util/streams/ops/FlatMapOp.java src/share/classes/java/util/streams/ops/FoldOp.java src/share/classes/java/util/streams/ops/ForEachOp.java src/share/classes/java/util/streams/ops/GroupByOp.java src/share/classes/java/util/streams/ops/MapOp.java src/share/classes/java/util/streams/ops/Nodes.java src/share/classes/java/util/streams/ops/ReduceByOp.java src/share/classes/java/util/streams/ops/SeedlessFoldOp.java src/share/classes/java/util/streams/ops/SliceOp.java src/share/classes/java/util/streams/ops/SortedOp.java src/share/classes/java/util/streams/ops/TeeOp.java src/share/classes/java/util/streams/ops/TreeUtils.java src/share/classes/java/util/streams/ops/UniqOp.java src/share/classes/java/util/streams/primitives/IntBlock.java src/share/classes/java/util/streams/primitives/IntCollectorOps.java src/share/classes/java/util/streams/primitives/IntFactory.java src/share/classes/java/util/streams/primitives/IntFilterOp.java src/share/classes/java/util/streams/primitives/IntForEachOp.java src/share/classes/java/util/streams/primitives/IntIterable.java src/share/classes/java/util/streams/primitives/IntIterator.java src/share/classes/java/util/streams/primitives/IntLimitOp.java src/share/classes/java/util/streams/primitives/IntMapOp.java src/share/classes/java/util/streams/primitives/IntNode.java src/share/classes/java/util/streams/primitives/IntNodeBuilder.java src/share/classes/java/util/streams/primitives/IntNodes.java src/share/classes/java/util/streams/primitives/IntPipeline.java src/share/classes/java/util/streams/primitives/IntPredicate.java src/share/classes/java/util/streams/primitives/IntSink.java src/share/classes/java/util/streams/primitives/IntSortedOp.java src/share/classes/java/util/streams/primitives/IntSpliterator.java src/share/classes/java/util/streams/primitives/IntStream.java src/share/classes/java/util/streams/primitives/IntSumOp.java src/share/classes/java/util/streams/primitives/IntTeeOp.java src/share/classes/java/util/streams/primitives/IntTerminalSink.java src/share/classes/java/util/streams/primitives/IntToArrayOp.java src/share/classes/java/util/streams/primitives/IntToIntegerOp.java src/share/classes/java/util/streams/primitives/IntTreeUtils.java src/share/classes/java/util/streams/primitives/IntUnaryOperator.java src/share/classes/java/util/streams/primitives/Primitives.java src/share/classes/java/util/streams/primitives/RefToIntMapOp.java test-ng/tests/org/openjdk/tests/java/util/LambdaTestHelpers.java test-ng/tests/org/openjdk/tests/java/util/streams/OpTestCase.java test-ng/tests/org/openjdk/tests/java/util/streams/StreamIntermediateOpTestScenario.java test-ng/tests/org/openjdk/tests/java/util/streams/ops/FilterOpTest.java test-ng/tests/org/openjdk/tests/java/util/streams/ops/FindFirstOpTest.java test-ng/tests/org/openjdk/tests/java/util/streams/ops/FlatMapOpTest.java test-ng/tests/org/openjdk/tests/java/util/streams/ops/IntNodeTest.java test-ng/tests/org/openjdk/tests/java/util/streams/ops/LimitOpTest.java test-ng/tests/org/openjdk/tests/java/util/streams/ops/MapOpTest.java test-ng/tests/org/openjdk/tests/java/util/streams/ops/NodeBuilderTest.java test-ng/tests/org/openjdk/tests/java/util/streams/ops/NodeTest.java test-ng/tests/org/openjdk/tests/java/util/streams/ops/PrimitiveOpsTests.java test-ng/tests/org/openjdk/tests/java/util/streams/ops/SkipOpTest.java test-ng/tests/org/openjdk/tests/java/util/streams/ops/SliceOpTest.java test-ng/tests/org/openjdk/tests/java/util/streams/ops/ToArrayOpTest.java test-ng/tests/org/openjdk/tests/java/util/streams/primitives/IntFilterOpTest.java test-ng/tests/org/openjdk/tests/java/util/streams/primitives/IntStreamIntermediateOpTestScenario.java test-ng/tests/org/openjdk/tests/java/util/streams/primitives/IntStreamTestData.java test-ng/tests/org/openjdk/tests/java/util/streams/primitives/IntStreamTestDataProvider.java
diffstat 77 files changed, 6249 insertions(+), 1455 deletions(-) [+]
line wrap: on
line diff
--- a/make/com/sun/crypto/provider/Makefile	Wed Nov 14 12:21:20 2012 -0500
+++ b/make/com/sun/crypto/provider/Makefile	Thu Nov 15 00:47:35 2012 -0500
@@ -114,7 +114,7 @@
 endif
 
 JAVAC_MAX_WARNINGS = false
-JAVAC_LINT_OPTIONS = -Xlint:all,-deprecation
+JAVAC_LINT_OPTIONS = -Xlint:all,-deprecation,-auxiliaryclass
 JAVAC_WARNINGS_FATAL = true
 include $(BUILDDIR)/common/Defs.gmk
 
--- a/make/com/sun/net/httpserver/Makefile	Wed Nov 14 12:21:20 2012 -0500
+++ b/make/com/sun/net/httpserver/Makefile	Thu Nov 15 00:47:35 2012 -0500
@@ -27,6 +27,7 @@
 PACKAGE = com.sun.net.httpserver
 PRODUCT = sun
 JAVAC_MAX_WARNINGS = true
+JAVAC_LINT_OPTIONS=-Xlint:all,-deprecation,-auxiliaryclass
 JAVAC_WARNINGS_FATAL = true
 include $(BUILDDIR)/common/Defs.gmk
 
--- a/make/docs/CORE_PKGS.gmk	Wed Nov 14 12:21:20 2012 -0500
+++ b/make/docs/CORE_PKGS.gmk	Thu Nov 15 00:47:35 2012 -0500
@@ -139,6 +139,7 @@
   java.util.spi                                  \
   java.util.streams                              \
   java.util.streams.ops                          \
+  java.util.streams.prmitives                    \
   java.util.zip                                  \
   javax.accessibility                            \
   javax.activation                               \
--- a/make/sun/security/other/Makefile	Wed Nov 14 12:21:20 2012 -0500
+++ b/make/sun/security/other/Makefile	Thu Nov 15 00:47:35 2012 -0500
@@ -27,7 +27,7 @@
 PACKAGE = sun.security.other
 PRODUCT = sun
 JAVAC_MAX_WARNINGS=true
-JAVAC_LINT_OPTIONS=-Xlint:all,-deprecation
+JAVAC_LINT_OPTIONS=-Xlint:all,-deprecation,-auxiliaryclass
 JAVAC_WARNINGS_FATAL=true
 include $(BUILDDIR)/common/Defs.gmk
 
--- a/makefiles/docs/CORE_PKGS.gmk	Wed Nov 14 12:21:20 2012 -0500
+++ b/makefiles/docs/CORE_PKGS.gmk	Thu Nov 15 00:47:35 2012 -0500
@@ -138,6 +138,7 @@
   java.util.regex                                \
   java.util.spi                                  \
   java.util.streams                              \
+  java.util.streams.primitives                   \
   java.util.streams.ops                          \
   java.util.zip                                  \
   javax.accessibility                            \
--- a/src/share/classes/java/util/Arrays.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/Arrays.java	Thu Nov 15 00:47:35 2012 -0500
@@ -3726,7 +3726,7 @@
         }
 
         @Override
-        public void accept(T t) {
+        public void apply(T t) {
             if (index >= length) {
                 throw new IndexOutOfBoundsException(Integer.toString(index));
             }
--- a/src/share/classes/java/util/concurrent/CountedCompleter.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/concurrent/CountedCompleter.java	Thu Nov 15 00:47:35 2012 -0500
@@ -142,9 +142,7 @@
  *
  * 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,
- * this version uses {@code helpComplete} to streamline assistance in
- * the execution of forked tasks.
+ * original task, and add a pending count for each fork. 
  *
  * <pre> {@code
  * class ForEach<E> ...
@@ -158,7 +156,7 @@
  *         }
  *         if (h > l)
  *             op.apply(array[l]);
- *         helpComplete();
+ *         tryComplete();
  *     }
  * }</pre>
  *
@@ -414,39 +412,6 @@
     }
 
     /**
-     * Identical to {@link #tryComplete}, but may additionally execute
-     * other tasks within the current computation (i.e., those
-     * with the same {@link #getRoot}.
-     */
-    public final void helpComplete() {
-        CountedCompleter<?> a = this, s = a;
-        for (int c;;) {
-            if ((c = a.pending) == 0) {
-                a.onCompletion(s);
-                if ((a = (s = a).completer) == null) {
-                    s.quietlyComplete();
-                    return;
-                }
-            }
-            else if (U.compareAndSwapInt(a, PENDING, c, c - 1)) {
-                CountedCompleter<?> root = a.getRoot();
-                Thread thread = Thread.currentThread();
-                ForkJoinPool.WorkQueue wq =
-                    (thread instanceof ForkJoinWorkerThread)?
-                    ((ForkJoinWorkerThread)thread).workQueue : null;
-                ForkJoinTask<?> t;
-                while ((t = (wq != null) ? wq.popCC(root) :
-                        ForkJoinPool.popCCFromCommonPool(root)) != null) {
-                    t.doExec();
-                    if (root.isDone())
-                        break;
-                }
-                return;
-            }
-        }
-    }
-
-    /**
      * 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. This
--- a/src/share/classes/java/util/concurrent/ForkJoinPool.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/concurrent/ForkJoinPool.java	Thu Nov 15 00:47:35 2012 -0500
@@ -11,7 +11,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.Random;
 import java.util.concurrent.AbstractExecutorService;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
@@ -20,10 +19,6 @@
 import java.util.concurrent.RunnableFuture;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.locks.AbstractQueuedSynchronizer;
-import java.util.concurrent.locks.Condition;
 
 /**
  * An {@link ExecutorService} for running {@link ForkJoinTask}s.
@@ -48,11 +43,7 @@
  * is not explicitly submitted to a specified pool. Using the common
  * pool normally reduces resource usage (its threads are slowly
  * reclaimed during periods of non-use, and reinstated upon subsequent
- * use).  The common pool is by default constructed with default
- * parameters, but these may be controlled by setting any or all of
- * the three properties {@code
- * java.util.concurrent.ForkJoinPool.common.{parallelism,
- * threadFactory, exceptionHandler}}.
+ * use).
  *
  * <p>For applications that require separate or custom pools, a {@code
  * ForkJoinPool} may be constructed with a given target parallelism
@@ -107,6 +98,16 @@
  *  </tr>
  * </table>
  *
+ * <p>The common pool is by default constructed with default
+ * parameters, but these may be controlled by setting three {@link
+ * System#getProperty 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
+ * Thread.UncaughtExceptionHandler}. Upon any error in establishing
+ * these settings, default parameters are used.
+ *
  * <p><b>Implementation notes</b>: This implementation restricts the
  * maximum number of running threads to 32767. Attempts to create
  * pools with greater than the maximum number result in
@@ -193,21 +194,24 @@
      * WorkQueues are also used in a similar way for tasks submitted
      * to the pool. We cannot mix these tasks in the same queues used
      * for work-stealing (this would contaminate lifo/fifo
-     * processing). Instead, we loosely associate submission queues
+     * 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 never
-     * take tasks, and they are multiplexed on to a finite number of
-     * shared work queues. However, classes are set up so that future
-     * extensions could allow submitters to optionally help perform
-     * tasks as well. 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 runState), because
-     * submitters encountering a busy queue move on to try or create
-     * other queues -- they block only when creating and registering
-     * new queues.
+     * 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
+     * creating and registering new queues.
      *
      * Management
      * ==========
@@ -229,11 +233,13 @@
      * and their negations (used for thresholding) to fit into 16bit
      * fields.
      *
-     * Field "runState" contains 32 bits needed to register and
-     * deregister WorkQueues, as well as to enable shutdown. It is
-     * only modified under a lock (normally briefly held, but
-     * occasionally protecting allocations and resizings) but even
-     * when locked remains available to check consistency.
+     * Field "plock" is a form of sequence lock with a saturating
+     * shutdown bit (similarly for per-queue "qlocks"), mainly
+     * protecting updates to the workQueues array, as well as to
+     * enable shutdown.  When used as a lock, it is normally only very
+     * briefly held, so is nearly always available after at most a
+     * brief spin, but we use a monitor-based backup strategy to
+     * blocking when needed.
      *
      * Recording WorkQueues.  WorkQueues are recorded in the
      * "workQueues" array that is created upon first use and expanded
@@ -242,9 +248,11 @@
      * by a lock but the array is otherwise concurrently readable, and
      * accessed directly.  To simplify index-based operations, the
      * array size is always a power of two, and all readers must
-     * tolerate null slots. Shared (submission) queues are at even
-     * indices, worker queues at odd indices. Grouping them together
-     * in this way simplifies and speeds up task scanning.
+     * tolerate null slots. Worker queues are at odd indices Shared
+     * (submission) queues are at even indices, up to a maximum of 64
+     * slots, to limit growth even if array needs to expand to add
+     * more workers. Grouping them together in this way simplifies and
+     * speeds up task scanning.
      *
      * All worker thread creation is on-demand, triggered by task
      * submissions, replacement of terminated workers, and/or
@@ -305,14 +313,19 @@
      *
      * 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.  When a submission is added or another worker adds a
-     * task to a queue that previously had fewer than two tasks, they
-     * signal waiting workers (or trigger creation of new ones if
-     * fewer than the given parallelism level -- see signalWork).
-     * These primary signals are buttressed by signals during rescans;
-     * together these cover the signals needed in cases when more
-     * tasks are pushed but untaken, and improve performance compared
-     * to having one thread wake up all workers.
+     * 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 is
+     * apparently empty, they signal waiting workers (or trigger
+     * creation of new ones if fewer than the given parallelism level
+     * -- see signalWork).  These primary signals are buttressed by
+     * signals whenever other threads scan for work or do not have a
+     * task to process. 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.
      *
      * Trimming workers. To release resources after periods of lack of
      * use, a worker starting to wait when the pool is quiescent will
@@ -323,8 +336,8 @@
      * periods of non-use.
      *
      * Shutdown and Termination. A call to shutdownNow atomically sets
-     * a runState bit and then (non-atomically) sets each worker's
-     * runState status, cancels all unprocessed tasks, and wakes up
+     * a plock bit and then (non-atomically) sets each worker's
+     * qlock status, cancels all unprocessed tasks, and wakes up
      * all waiting workers.  Detecting whether termination should
      * commence after a non-abrupt shutdown() call requires more work
      * and bookkeeping. We need consensus about quiescence (i.e., that
@@ -352,13 +365,13 @@
      *      method tryCompensate() may create or re-activate a spare
      *      thread to compensate for blocked joiners until they unblock.
      *
-     * A third form (implemented in tryRemoveAndExec and
-     * tryPollForAndExec) amounts to helping a hypothetical
-     * compensator: If we can readily tell that a possible action of a
-     * compensator is to steal and execute the task being joined, the
-     * joining thread can do so directly, without the need for a
-     * compensation thread (although at the expense of larger run-time
-     * stacks, but the tradeoff is typically worthwhile).
+     * A third form (implemented in tryRemoveAndExec) amounts to
+     * helping a hypothetical compensator: If we can readily tell that
+     * a possible action of a compensator is to steal and execute the
+     * task being joined, the joining thread can do so directly,
+     * without the need for a compensation thread (although at the
+     * expense of larger run-time stacks, but the tradeoff is
+     * typically worthwhile).
      *
      * The ManagedBlocker extension API can't use helping so relies
      * only on compensation in method awaitBlocker.
@@ -393,6 +406,12 @@
      * to find work (see MAX_HELP) and fall back to suspending the
      * worker and if necessary replacing it with another.
      *
+     * Helping actions for CountedCompleters are much simpler: Method
+     * helpComplete can take and execute any task with the same root
+     * as the task being waited on. However, this still entails some
+     * traversal of completer chains, so is less efficient than using
+     * CountedCompleters without explicit joins.
+     *
      * It is impossible to keep exactly the target parallelism number
      * of threads running at any given time.  Determining the
      * existence of conservatively safe helping targets, the
@@ -414,30 +433,41 @@
      * intractable) game with an opponent that may choose the worst
      * (for us) active thread to stall at any time.  We take several
      * precautions to bound losses (and thus bound gains), mainly in
-     * methods tryCompensate and awaitJoin: (1) We only try
-     * compensation after attempting enough helping steps (measured
-     * via counting and timing) that we have already consumed the
-     * estimated cost of creating and activating a new thread.  (2) We
-     * allow up to 50% of threads to be blocked before initially
-     * adding any others, and unless completely saturated, check that
-     * some work is available for a new worker before adding. Also, we
-     * create up to only 50% more threads until entering a mode that
-     * only adds a thread if all others are possibly blocked.  All
-     * together, this means that we might be half as fast to react,
-     * and create half as many threads as possible in the ideal case,
-     * but present vastly fewer anomalies in all other cases compared
-     * to both more aggressive and more conservative alternatives.
+     * methods tryCompensate and awaitJoin.
      *
-     * Style notes: There is a lot of representation-level coupling
-     * among classes ForkJoinPool, ForkJoinWorkerThread, and
-     * ForkJoinTask.  The fields of WorkQueue maintain data structures
-     * managed by ForkJoinPool, so are directly accessed.  There is
-     * little point trying to reduce this, since any associated future
-     * changes in representations will need to be accompanied by
-     * algorithmic changes anyway. Several methods intrinsically
-     * sprawl because they must accumulate sets of consistent reads of
-     * volatiles held in local variables.  Methods signalWork() and
-     * scan() are the main bottlenecks, so are especially heavily
+     * Common Pool
+     * ===========
+     *
+     * The static commonPool 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
+     * allocation. Most bootstrapping occurs within method
+     * 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
+     * submissions are to the common pool -- if not, externalHelpJoin
+     * returns quicky (at the most helping to signal some common pool
+     * workers). These submitters would otherwise be blocked waiting
+     * for completion, so the extra effort (with liberally sprinkled
+     * task status checks) in inapplicable cases amounts to an odd
+     * form of limited spin-wait before blocking in ForkJoinTask.join.
+     *
+     * Style notes
+     * ===========
+     *
+     * There is a lot of representation-level coupling among classes
+     * ForkJoinPool, ForkJoinWorkerThread, and ForkJoinTask.  The
+     * fields of WorkQueue maintain data structures managed by
+     * ForkJoinPool, so are directly accessed.  There is little point
+     * trying to reduce this, since any associated future changes in
+     * representations will need to be accompanied by algorithmic
+     * changes anyway. Several methods intrinsically sprawl because
+     * they must accumulate sets of consistent reads of volatiles held
+     * in local variables.  Methods signalWork() and scan() are the
+     * main bottlenecks, so are especially heavily
      * micro-optimized/mangled.  There are lots of inline assignments
      * (of form "while ((local = field) != 0)") which are usually the
      * simplest way to ensure the required read orderings (which are
@@ -445,7 +475,8 @@
      * declarations of these locals at the heads of methods or blocks.
      * There are several occurrences of the unusual "do {} while
      * (!cas...)"  which is the simplest way to force an update of a
-     * CAS'ed variable. There are also other coding oddities that help
+     * CAS'ed variable. There are also other coding oddities (including
+     * several unnecessary-looking hoisted null checks) that help
      * some methods perform reasonably even when interpreted (not
      * compiled).
      *
@@ -508,6 +539,7 @@
      * actually do anything beyond having a unique identity.
      */
     static final class EmptyTask extends ForkJoinTask<Void> {
+        private static final long serialVersionUID = -7721805057305804111L;
         EmptyTask() { status = ForkJoinTask.NORMAL; } // force done
         public final Void getRawResult() { return null; }
         public final void setRawResult(Void x) {}
@@ -528,27 +560,31 @@
      *
      * Field "top" is the index (mod array.length) of the next queue
      * slot to push to or pop from. It is written only by owner thread
-     * for push, or under lock for trySharedPush, and accessed by
-     * other threads only after reading (volatile) base.  Both top and
-     * base are allowed to wrap around on overflow, but (top - base)
-     * (or more commonly -(base - top) to force volatile read of base
-     * before top) still estimates size.
+     * for push, or under lock for external/shared push, and accessed
+     * by other threads only after reading (volatile) base.  Both top
+     * and base are allowed to wrap around on overflow, but (top -
+     * base) (or more commonly -(base - top) to force volatile read of
+     * base before top) still estimates size. The lock ("qlock") is
+     * forced to -1 on termination, causing all further lock attempts
+     * to fail. (Note: we don't need CAS for termination state because
+     * upon pool shutdown, all shared-queues will stop being used
+     * anyway.)  Nearly all lock bodies are set up so that exceptions
+     * within lock bodies are "impossible" (modulo JVM errors that
+     * would cause failure anyway.)
      *
      * The array slots are read and written using the emulation of
      * volatiles/atomics provided by Unsafe. Insertions must in
      * general use putOrderedObject as a form of releasing store to
      * ensure that all writes to the task object are ordered before
-     * its publication in the queue. (Although we can avoid one case
-     * of this when locked in trySharedPush.) All removals entail a
-     * CAS to null.  The array is always a power of two. To ensure
-     * safety of Unsafe array operations, all accesses perform
-     * explicit null checks and implicit bounds checks via
-     * power-of-two masking.
+     * its publication in the queue.  All removals entail a CAS to
+     * null.  The array is always a power of two. To ensure safety of
+     * Unsafe array operations, all accesses perform explicit null
+     * checks and implicit bounds checks via power-of-two masking.
      *
      * In addition to basic queuing support, this class contains
      * fields described elsewhere to control execution. It turns out
-     * to work better memory-layout-wise to include them in this
-     * class rather than a separate class.
+     * to work better memory-layout-wise to include them in this class
+     * rather than a separate class.
      *
      * Performance on most platforms is very sensitive to placement of
      * instances of both WorkQueues and their arrays -- we absolutely
@@ -564,8 +600,8 @@
      * 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 (via method growArray) by workers before
-     * use. Others are allocated on first use.
+     * arrays are initialized by workers before use. Others are
+     * allocated on first use.
      */
     static final class WorkQueue {
         /**
@@ -588,16 +624,14 @@
          */
         static final int MAXIMUM_QUEUE_CAPACITY = 1 << 26; // 64M
 
-        volatile long totalSteals; // cumulative number of steals
         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 rescans;               // remaining scans until block
-        int nsteals;               // top-level task executions since last idle
         final int mode;            // lifo, fifo, or shared
+        int nsteals;               // cumulative number of steals
         int poolIndex;             // index of this queue in pool (or 0)
         int stealHint;             // index of most recent known stealer
-        volatile int runState;     // 1: locked, -1: terminate; else 0
+        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
         ForkJoinTask<?>[] array;   // the elements (initially unallocated)
@@ -619,75 +653,82 @@
         }
 
         /**
-         * Returns the approximate number of tasks in the queue.
-         */
-        final int queueSize() {
-            int n = base - top;       // non-owner callers must read base first
-            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.
-         */
-        final boolean isEmpty() {
-            ForkJoinTask<?>[] a; int m, s;
-            int n = base - (s = top);
-            return (n >= 0 ||
-                    (n == -1 &&
-                     ((a = array) == null ||
-                      (m = a.length - 1) < 0 ||
-                      U.getObjectVolatile
-                      (a, ((m & (s - 1)) << ASHIFT) + ABASE) == null)));
-        }
-
-        /**
          * Pushes a task. Call only by owner in unshared queues.
+         * Cases needing resizing or rejection are relyaed to fullPush
+         * (that also handles shared queues).
          *
          * @param task the task. Caller must ensure non-null.
          * @throw RejectedExecutionException if array cannot be resized
          */
         final void push(ForkJoinTask<?> task) {
-            ForkJoinTask<?>[] a; ForkJoinPool p;
-            int s = top, m, n;
-            if ((a = array) != null) {    // ignore if queue removed
+            ForkJoinPool p; ForkJoinTask<?>[] a;
+            int s = top, n;
+            if ((a = array) != null && a.length > (n = s + 1 - base)) {
                 U.putOrderedObject
-                    (a, (((m = a.length - 1) & s) << ASHIFT) + ABASE, task);
-                if ((n = (top = s + 1) - base) <= 2) {
-                    if ((p = pool) != null)
-                        p.signalWork();
-                }
-                else if (n >= m)
-                    growArray(true);
+                    (a, (((a.length - 1) & s) << ASHIFT) + ABASE, task);
+                top = s + 1;
+                if (n <= 1 && (p = pool) != null)
+                    p.signalWork(this, 1);
             }
+            else
+                fullPush(task, true);
         }
 
         /**
          * Pushes a task if lock is free and array is either big
-         * enough or can be resized to be big enough.
+         * enough or can be resized to be big enough. Note: a
+         * specialization of a common fast path of this method is in
+         * ForkJoinPool.externalPush. When called from a FJWT queue,
+         * this can fail only if the pool has been shut down or
+         * an out of memory error.
          *
          * @param task the task. Caller must ensure non-null.
-         * @return true if submitted
+         * @param owned if true, throw RJE on failure
          */
-        final boolean trySharedPush(ForkJoinTask<?> task) {
-            boolean submitted = false;
-            if (runState == 0 && U.compareAndSwapInt(this, RUNSTATE, 0, 1)) {
-                ForkJoinTask<?>[] a = array;
-                int s = top;
-                try {
-                    if ((a != null && a.length > s + 1 - base) ||
-                        (a = growArray(false)) != null) { // must presize
-                        int j = (((a.length - 1) & s) << ASHIFT) + ABASE;
-                        U.putObject(a, (long)j, task);    // don't need "ordered"
-                        top = s + 1;
-                        submitted = true;
+        final boolean fullPush(ForkJoinTask<?> task, boolean owned) {
+            ForkJoinPool p; ForkJoinTask<?>[] a;
+            if (owned) {
+                if (qlock < 0) // must be shutting down
+                    throw new RejectedExecutionException();
+            }
+            else if (!U.compareAndSwapInt(this, QLOCK, 0, 1))
+                return false;
+            try {
+                int s = top, oldLen, len;
+                if ((a = array) == null)
+                    a = array = new ForkJoinTask<?>[len=INITIAL_QUEUE_CAPACITY];
+                else if ((oldLen = a.length) > s + 1 - base)
+                    len = oldLen;
+                else if ((len = oldLen << 1) > MAXIMUM_QUEUE_CAPACITY)
+                    throw new RejectedExecutionException("Capacity exceeded");
+                else {
+                    int oldMask, b;
+                    ForkJoinTask<?>[] oldA = a;
+                    a = array = new ForkJoinTask<?>[len];
+                    if ((oldMask = oldLen - 1) >= 0 && s - (b = base) > 0) {
+                        int mask = len - 1;
+                        do {
+                            ForkJoinTask<?> x;
+                            int oldj = ((b & oldMask) << ASHIFT) + ABASE;
+                            int j    = ((b &    mask) << ASHIFT) + ABASE;
+                            x = (ForkJoinTask<?>)
+                                U.getObjectVolatile(oldA, oldj);
+                            if (x != null &&
+                                U.compareAndSwapObject(oldA, oldj, x, null))
+                                U.putObjectVolatile(a, j, x);
+                        } while (++b != s);
                     }
-                } finally {
-                    runState = 0;                         // unlock
                 }
+                U.putOrderedObject
+                    (a, (((len - 1) & s) << ASHIFT) + ABASE, task);
+                top = s + 1;
+            } finally {
+                if (!owned)
+                    qlock = 0;
             }
-            return submitted;
+            if ((p = pool) != null)
+                p.signalWork(this, 1);
+            return true;
         }
 
         /**
@@ -710,90 +751,6 @@
             return null;
         }
 
-        final ForkJoinTask<?> sharedPop() {
-            ForkJoinTask<?> task = null;
-            if (runState == 0 && U.compareAndSwapInt(this, RUNSTATE, 0, 1)) {
-                try {
-                    ForkJoinTask<?>[] a; int m;
-                    if ((a = array) != null && (m = a.length - 1) >= 0) {
-                        for (int s; (s = top - 1) - base >= 0;) {
-                            long j = ((m & s) << ASHIFT) + ABASE;
-                            ForkJoinTask<?> t =
-                                (ForkJoinTask<?>)U.getObject(a, j);
-                            if (t == null)
-                                break;
-                            if (U.compareAndSwapObject(a, j, t, null)) {
-                                top = s;
-                                task = t;
-                                break;
-                            }
-                        }
-                    }
-                } finally {
-                    runState = 0;
-                }
-            }
-            return task;
-        }
-
-        /**
-         * Version of pop that takes top element only if it
-         * its root is the given CountedCompleter.
-         */
-        final ForkJoinTask<?> popCC(CountedCompleter<?> root) {
-            ForkJoinTask<?>[] a; int m;
-            if (root != null && (a = array) != null && (m = a.length - 1) >= 0) {
-                for (int s; (s = top - 1) - base >= 0;) {
-                    long j = ((m & s) << ASHIFT) + ABASE;
-                    ForkJoinTask<?> t =
-                        (ForkJoinTask<?>)U.getObject(a, j);
-                    if (t == null || !(t instanceof CountedCompleter) ||
-                        ((CountedCompleter<?>)t).getRoot() != root)
-                        break;
-                    if (U.compareAndSwapObject(a, j, t, null)) {
-                        top = s;
-                        return t;
-                    }
-                    if (root.status < 0)
-                        break;
-                }
-            }
-            return null;
-        }
-
-        /**
-         * Shared version of popCC
-         */
-        final ForkJoinTask<?> sharedPopCC(CountedCompleter<?> root) {
-            ForkJoinTask<?> task = null;
-            if (root != null &&
-                runState == 0 && U.compareAndSwapInt(this, RUNSTATE, 0, 1)) {
-                try {
-                    ForkJoinTask<?>[] a; int m;
-                    if ((a = array) != null && (m = a.length - 1) >= 0) {
-                        for (int s; (s = top - 1) - base >= 0;) {
-                            long j = ((m & s) << ASHIFT) + ABASE;
-                            ForkJoinTask<?> t =
-                                (ForkJoinTask<?>)U.getObject(a, j);
-                            if (t == null || !(t instanceof CountedCompleter) ||
-                                ((CountedCompleter<?>)t).getRoot() != root)
-                                break;
-                            if (U.compareAndSwapObject(a, j, t, null)) {
-                                top = s;
-                                task = t;
-                                break;
-                            }
-                            if (root.status < 0)
-                                break;
-                        }
-                    }
-                } finally {
-                    runState = 0;
-                }
-            }
-            return task;
-        }
-
         /**
          * Takes a task in FIFO order if b is base of queue and a task
          * can be claimed without contention. Specialized versions
@@ -831,7 +788,7 @@
                 else if (base == b) {
                     if (b + 1 == top)
                         break;
-                    Thread.yield(); // wait for lagging update
+                    Thread.yield(); // wait for lagging update (very rare)
                 }
             }
             return null;
@@ -858,6 +815,7 @@
 
         /**
          * Pops the given task only if it is at the current top.
+         * (A shared version is available only via FJP.tryExternalUnpush)
          */
         final boolean tryUnpush(ForkJoinTask<?> t) {
             ForkJoinTask<?>[] a; int s;
@@ -871,79 +829,6 @@
         }
 
         /**
-         * Version of tryUnpush for shared queues; called by non-FJ
-         * submitters after prechecking that task probably exists.
-         */
-        final boolean trySharedUnpush(ForkJoinTask<?> t) {
-            boolean success = false;
-            if (runState == 0 && U.compareAndSwapInt(this, RUNSTATE, 0, 1)) {
-                try {
-                    ForkJoinTask<?>[] a; int s;
-                    if ((a = array) != null && (s = top) != base &&
-                        U.compareAndSwapObject
-                        (a, (((a.length - 1) & --s) << ASHIFT) + ABASE, t, null)) {
-                        top = s;
-                        success = true;
-                    }
-                } finally {
-                    runState = 0;                         // unlock
-                }
-            }
-            return success;
-        }
-
-        /**
-         * Polls the given task only if it is at the current base.
-         */
-        final boolean pollFor(ForkJoinTask<?> task) {
-            ForkJoinTask<?>[] a; int b;
-            if ((b = base) - top < 0 && (a = array) != null) {
-                int j = (((a.length - 1) & b) << ASHIFT) + ABASE;
-                if (U.getObjectVolatile(a, j) == task && base == b &&
-                    U.compareAndSwapObject(a, j, task, null)) {
-                    base = b + 1;
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * 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.
-         *
-         * @param rejectOnFailure if true, throw exception if capacity
-         * exceeded (relayed ultimately to user); else return null.
-         */
-        final ForkJoinTask<?>[] growArray(boolean rejectOnFailure) {
-            ForkJoinTask<?>[] oldA = array;
-            int size = oldA != null ? oldA.length << 1 : INITIAL_QUEUE_CAPACITY;
-            if (size <= MAXIMUM_QUEUE_CAPACITY) {
-                int oldMask, t, b;
-                ForkJoinTask<?>[] a = array = new ForkJoinTask<?>[size];
-                if (oldA != null && (oldMask = oldA.length - 1) >= 0 &&
-                    (t = top) - (b = base) > 0) {
-                    int mask = size - 1;
-                    do {
-                        ForkJoinTask<?> x;
-                        int oldj = ((b & oldMask) << ASHIFT) + ABASE;
-                        int j    = ((b &    mask) << ASHIFT) + ABASE;
-                        x = (ForkJoinTask<?>)U.getObjectVolatile(oldA, oldj);
-                        if (x != null &&
-                            U.compareAndSwapObject(oldA, oldj, x, null))
-                            U.putObjectVolatile(a, j, x);
-                    } while (++b != t);
-                }
-                return a;
-            }
-            else if (!rejectOnFailure)
-                return null;
-            else
-                throw new RejectedExecutionException("Queue capacity exceeded");
-        }
-
-        /**
          * Removes and cancels all known tasks, ignoring any exceptions.
          */
         final void cancelAll() {
@@ -967,7 +852,22 @@
             return seed = r ^= r << 5;
         }
 
-        // Execution methods
+        /**
+         * Provides a more accurate estimate of size than (top - base)
+         * by ordering reads and checking whether a near-empty queue
+         * has at least one unclaimed task.
+         */
+        final int queueSize() {
+            ForkJoinTask<?>[] a; int k, s, n;
+            return ((n = base - (s = top)) < 0 &&
+                    (n != -1 ||
+                     ((a = array) != null && (k = a.length) > 0 &&
+                      U.getObject
+                      (a, (long)((((k - 1) & (s - 1)) << ASHIFT) + ABASE)) != null))) ?
+                -n : 0;
+        }
+
+        // Specialized execution methods
 
         /**
          * Pops and runs tasks until empty.
@@ -996,16 +896,14 @@
         }
 
         /**
-         * If present, removes from queue and executes the given task, or
-         * any other cancelled task. Returns (true) immediately on any CAS
+         * 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.
          *
-         * @return 0 if no progress can be made, else positive
-         * (this unusual convention simplifies use with tryHelpStealer.)
+         * @return false if no progress can be made, else true;
          */
-        final int tryRemoveAndExec(ForkJoinTask<?> task) {
-            int stat = 1;
-            boolean removed = false, empty = true;
+        final boolean tryRemoveAndExec(ForkJoinTask<?> task) {
+            boolean stat = true, removed = false, empty = true;
             ForkJoinTask<?>[] a; int m, s, b, n;
             if ((a = array) != null && (m = a.length - 1) >= 0 &&
                 (n = (s = top) - (b = base)) > 0) {
@@ -1035,7 +933,7 @@
                     }
                     if (--n == 0) {
                         if (!empty && base == b)
-                            stat = 0;
+                            stat = false;
                         break;
                     }
                 }
@@ -1046,21 +944,53 @@
         }
 
         /**
+         * Polls for and executes 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) {
+                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();
+                            return true;
+                        }
+                        else
+                            break; // restart
+                    }
+                    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.
          */
         final void runTask(ForkJoinTask<?> t) {
             if (t != null) {
-                currentSteal = t;
-                t.doExec();
+                (currentSteal = t).doExec();
+                currentSteal = null;
+                if (++nsteals < 0) {     // spill on overflow
+                    ForkJoinPool p;
+                    if ((p = pool) != null)
+                        p.collectStealCount(this);
+                }
                 if (top != base) {       // process remaining local tasks
                     if (mode == 0)
                         popAndExecAll();
                     else
                         pollAndExecAll();
                 }
-                ++nsteals;
-                currentSteal = null;
             }
         }
 
@@ -1070,8 +1000,7 @@
         final void runSubtask(ForkJoinTask<?> t) {
             if (t != null) {
                 ForkJoinTask<?> ps = currentSteal;
-                currentSteal = t;
-                t.doExec();
+                (currentSteal = t).doExec();
                 currentSteal = ps;
             }
         }
@@ -1106,7 +1035,7 @@
 
         // Unsafe mechanics
         private static final sun.misc.Unsafe U;
-        private static final long RUNSTATE;
+        private static final long QLOCK;
         private static final int ABASE;
         private static final int ASHIFT;
         static {
@@ -1115,8 +1044,8 @@
                 U = sun.misc.Unsafe.getUnsafe();
                 Class<?> k = WorkQueue.class;
                 Class<?> ak = ForkJoinTask[].class;
-                RUNSTATE = U.objectFieldOffset
-                    (k.getDeclaredField("runState"));
+                QLOCK = U.objectFieldOffset
+                    (k.getDeclaredField("qlock"));
                 ABASE = U.arrayBaseOffset(ak);
                 s = U.arrayIndexScale(ak);
             } catch (Exception e) {
@@ -1131,7 +1060,7 @@
     /**
      * 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 doSubmit. In the future, this may
+     * submission queues in method externalPush. In the future, this may
      * also incorporate a means to implement different task rejection
      * and resubmission policies.
      *
@@ -1139,23 +1068,18 @@
      * 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. This is done during
-     * registration for workers, but requires a separate AtomicInteger
-     * for submitters. Seeds are then randomly modified upon
-     * collisions using xorshifts, which requires a non-zero seed.
+     * 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 = nextSubmitterSeed.getAndAdd(SEED_INCREMENT);
-            seed = (s == 0) ? 1 : s; // ensure non-zero
-        }
+        Submitter(int s) { seed = s; }
     }
 
-    /** ThreadLocal class for Submitters */
-    static final class ThreadSubmitter extends ThreadLocal<Submitter> {
-        public Submitter initialValue() { return new Submitter(); }
-    }
+    /** Property prefix for constructing common pool */
+    private static final String propPrefix =
+        "java.util.concurrent.ForkJoinPool.common.";
 
     // static fields (initialized in static initializer below)
 
@@ -1166,35 +1090,15 @@
     public static final ForkJoinWorkerThreadFactory
         defaultForkJoinWorkerThreadFactory;
 
-
-    /** Property prefix for constructing common pool */
-    private static final String propPrefix =
-        "java.util.concurrent.ForkJoinPool.common.";
-
     /**
      * Common (static) pool. Non-null for public use unless a static
-     * construction exception, but internal usages must null-check on
-     * use.
+     * construction exception, but internal usages null-check on use
+     * to paranoically avoid potential initialization circularities
+     * as well as to simplify generated code.
      */
     static final ForkJoinPool commonPool;
 
     /**
-     * Common pool parallelism. Must equal commonPool.parallelism.
-     */
-    static final int commonPoolParallelism;
-
-    /**
-     * Generator for assigning sequence numbers as pool names.
-     */
-    private static final AtomicInteger poolNumberGenerator;
-
-    /**
-     * Generator for initial hashes/seeds for submitters. Accessed by
-     * Submitter class constructor.
-     */
-    static final AtomicInteger nextSubmitterSeed;
-
-    /**
      * Permission required for callers of methods that may start or
      * kill threads.
      */
@@ -1204,29 +1108,49 @@
      * 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.
+     * Lazily initialized on first submission (but null-checked
+     * in other contexts to avoid unnecessary initialization).
      */
-    private static final ThreadSubmitter submitters;
+    static final ThreadLocal<Submitter> submitters;
+
+    /**
+     * Common pool parallelism. Must equal commonPool.parallelism.
+     */
+    static final int commonPoolParallelism;
+
+    /**
+     * Sequence number for creating workerNamePrefix.
+     */
+    private static int poolNumberSequence;
+
+    /**
+     * Return the next sequence number. We don't expect this to
+     * ever contend so use simple builtin sync.
+     */
+    private static final synchronized int nextPoolId() {
+        return ++poolNumberSequence;
+    }
 
     // static constants
 
     /**
-     * Initial timeout value (in nanoseconds) for the thread triggering
-     * quiescence to park waiting for new work. On timeout, the thread
-     * will instead try to shrink the number of workers.
+     * Initial timeout value (in nanoseconds) for the thread
+     * triggering quiescence to park waiting for new work. On timeout,
+     * the thread will instead try to shrink the number of
+     * workers. The value should be large enough to avoid overly
+     * aggressive shrinkage during most transient stalls (long GCs
+     * etc).
      */
-    private static final long IDLE_TIMEOUT      = 1000L * 1000L * 1000L; // 1sec
+    private static final long IDLE_TIMEOUT      = 2000L * 1000L * 1000L; // 2sec
 
     /**
      * Timeout value when there are more threads than parallelism level
      */
-    private static final long FAST_IDLE_TIMEOUT =  100L * 1000L * 1000L;
+    private static final long FAST_IDLE_TIMEOUT =  200L * 1000L * 1000L;
 
     /**
      * The maximum stolen->joining link depth allowed in method
-     * tryHelpStealer.  Must be a power of two. This value also
-     * controls the maximum number of times to try to help join a task
-     * without any apparent progress or change in pool state before
-     * giving up and blocking (see awaitJoin).  Depths for legitimate
+     * tryHelpStealer.  Must be a power of two.  Depths for legitimate
      * chains are unbounded, but we use a fixed constant to avoid
      * (otherwise unchecked) cycles and to bound staleness of
      * traversal parameters at the expense of sometimes blocking when
@@ -1235,16 +1159,6 @@
     private static final int MAX_HELP = 64;
 
     /**
-     * Secondary time-based bound (in nanosecs) for helping attempts
-     * before trying compensated blocking in awaitJoin. Used in
-     * conjunction with MAX_HELP to reduce variance due to different
-     * polling rates associated with different helping options. The
-     * value should roughly approximate the time required to create
-     * and/or activate a worker thread.
-     */
-    private static final long COMPENSATION_DELAY = 1L << 18; // ~0.25 millisec
-
-    /**
      * Increment for seed generators. See class ThreadLocal for
      * explanation.
      */
@@ -1278,14 +1192,14 @@
      * scan for them to avoid queuing races. Note however that
      * eventCount updates lag releases so usage requires care.
      *
-     * Field runState is an int packed with:
+     * Field plock is an int packed with:
      * SHUTDOWN: true if shutdown is enabled (1 bit)
-     * SEQ:  a sequence number updated upon (de)registering workers (30 bits)
-     * INIT: set true after workQueues array construction (1 bit)
+     * SEQ:  a sequence lock, with PL_LOCK bit set if locked (30 bits)
+     * SIGNAL: set when threads may be waiting on the lock (1 bit)
      *
      * The sequence number enables simple consistency checks:
      * Staleness of read-only operations on the workQueues array can
-     * be checked by comparing runState before vs after the reads.
+     * be checked by comparing plock before vs after the reads.
      */
 
     // bit positions/shifts for fields
@@ -1297,7 +1211,8 @@
     // bounds
     private static final int  SMASK      = 0xffff;  // short bits
     private static final int  MAX_CAP    = 0x7fff;  // max #workers - 1
-    private static final int  SQMASK     = 0xfffe;  // even short bits
+    private static final int  EVENMASK   = 0xfffe;  // even short bits
+    private static final int  SQMASK     = 0x007e;  // max 64 (even) slots
     private static final int  SHORT_SIGN = 1 << 15;
     private static final int  INT_SIGN   = 1 << 31;
 
@@ -1322,8 +1237,11 @@
     private static final int E_MASK      = 0x7fffffff; // no STOP_BIT
     private static final int E_SEQ       = 1 << EC_SHIFT;
 
-    // runState bits
+    // plock bits
     private static final int SHUTDOWN    = 1 << 31;
+    private static final int PL_LOCK     = 2;
+    private static final int PL_SIGNAL   = 1;
+    private static final int PL_SPINS    = 1 << 8;
 
     // access mode for WorkQueue
     static final int LIFO_QUEUE          =  0;
@@ -1338,90 +1256,69 @@
      * declaration order and may differ across JVMs, but the following
      * empirically works OK on current JVMs.
      */
-
     volatile long stealCount;                  // collects worker counts
     volatile long ctl;                         // main pool control
     final int parallelism;                     // parallelism level
     final int localMode;                       // per-worker scheduling mode
-    volatile int nextWorkerNumber;             // to create worker name string
-    final int submitMask;                      // submit queue index bound
-    int nextSeed;                              // for initializing worker seeds
-    volatile int mainLock;                     // spinlock for array updates
-    volatile int runState;                     // shutdown status and seq
+    volatile int indexSeed;                    // worker/submitter index seed
+    volatile int plock;                        // shutdown status and seqLock
     WorkQueue[] workQueues;                    // main registry
     final ForkJoinWorkerThreadFactory factory; // factory for new workers
     final Thread.UncaughtExceptionHandler ueh; // per-worker UEH
     final String workerNamePrefix;             // to create worker name string
 
     /*
-     * Mechanics for main lock protecting worker array updates.  Uses
-     * the same strategy as ConcurrentHashMap bins -- a spinLock for
-     * normal cases, but falling back to builtin lock when (rarely)
-     * needed.  See internal ConcurrentHashMap documentation for
-     * explanation.
+     * Acquires the plock lock to protect worker array and related
+     * updates. This method is called only if an initial CAS on plock
+     * fails. This acts as a spinLock for normal cases, but falls back
+     * to builtin monitor to block when (rarely) needed. This would be
+     * a terrible idea for a highly contended lock, but works fine as
+     * a more conservative alternative to a pure spinlock.  See
+     * internal ConcurrentHashMap documentation for further
+     * explanation of nearly the same construction.
      */
-
-    static final int LOCK_WAITING = 2; // bit to indicate need for signal
-    static final int MAX_LOCK_SPINS = 1 << 8;
-
-    private void tryAwaitMainLock() {
-        int spins = MAX_LOCK_SPINS, r = 0, h;
-        while (((h = mainLock) & 1) != 0) {
-            if (r == 0)
+    private int acquirePlock() {
+        int spins = PL_SPINS, r = 0, ps, nps;
+        for (;;) {
+            if (((ps = plock) & PL_LOCK) == 0 &&
+                U.compareAndSwapInt(this, PLOCK, ps, nps = ps + PL_LOCK))
+                return nps;
+            else if (r == 0)
                 r = ThreadLocalRandom.current().nextInt(); // randomize spins
             else if (spins >= 0) {
                 r ^= r << 1; r ^= r >>> 3; r ^= r << 10; // xorshift
                 if (r >= 0)
                     --spins;
             }
-            else if (U.compareAndSwapInt(this, MAINLOCK, h, h | LOCK_WAITING)) {
-                synchronized (this) {
-                    if ((mainLock & LOCK_WAITING) != 0) {
+            else if (U.compareAndSwapInt(this, PLOCK, ps, ps | PL_SIGNAL)) {
+                synchronized(this) {
+                    if ((plock & PL_SIGNAL) != 0) {
                         try {
                             wait();
                         } catch (InterruptedException ie) {
-                            Thread.currentThread().interrupt();
+                            try {
+                                Thread.currentThread().interrupt();
+                            } catch (SecurityException ignore) {
+                            }
                         }
                     }
                     else
-                        notifyAll(); // possibly won race vs signaller
+                        notifyAll();
                 }
-                break;
             }
         }
     }
 
-    //  Creating, registering, and deregistering workers
-
     /**
-     * Tries to create and start a worker
+     * Unlocks and signals any thread waiting for plock. Called only
+     * when CAS of seq value for unlock fails.
      */
-    private void addWorker() {
-        Throwable ex = null;
-        ForkJoinWorkerThread wt = null;
-        try {
-            if ((wt = factory.newThread(this)) != null) {
-                wt.start();
-                return;
-            }
-        } catch (Throwable e) {
-            ex = e;
-        }
-        deregisterWorker(wt, ex); // adjust counts etc on failure
+    private void releasePlock(int ps) {
+        plock = ps;
+        synchronized(this) { notifyAll(); }
     }
 
-    /**
-     * Callback from ForkJoinWorkerThread constructor to assign a
-     * public name. This must be separate from registerWorker because
-     * it is called during the "super" constructor call in
-     * ForkJoinWorkerThread.
-     */
-    final String nextWorkerName() {
-        int n;
-        do {} while(!U.compareAndSwapInt(this, NEXTWORKERNUMBER,
-                                         n = nextWorkerNumber, ++n));
-        return workerNamePrefix.concat(Integer.toString(n));
-    }
+    //  Registering and deregistering workers
 
     /**
      * Callback from ForkJoinWorkerThread constructor to establish its
@@ -1433,20 +1330,23 @@
      * @param w the worker's queue
      */
     final void registerWorker(WorkQueue w) {
-        while (!U.compareAndSwapInt(this, MAINLOCK, 0, 1))
-            tryAwaitMainLock();
+        int s, ps; // generate a rarely colliding candidate index seed
+        do {} while (!U.compareAndSwapInt(this, INDEXSEED,
+                                          s = indexSeed, s += SEED_INCREMENT) ||
+                     s == 0); // skip 0
+        if (((ps = plock) & PL_LOCK) != 0 ||
+            !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+            ps = acquirePlock();
+        int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
         try {
             WorkQueue[] ws;
-            if ((ws = workQueues) == null)
-                ws = workQueues = new WorkQueue[submitMask + 1];
-            if (w != null) {
-                int rs, n =  ws.length, m = n - 1;
-                int s = nextSeed += SEED_INCREMENT; // rarely-colliding sequence
-                w.seed = (s == 0) ? 1 : s;          // ensure non-zero seed
+            if (w != null && (ws = workQueues) != null) {
+                w.seed = s;
+                int n = ws.length, m = n - 1;
                 int r = (s << 1) | 1;               // use odd-numbered indices
                 if (ws[r &= m] != null) {           // collision
                     int probes = 0;                 // step by approx half size
-                    int step = (n <= 4) ? 2 : ((n >>> 1) & SQMASK) + 2;
+                    int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2;
                     while (ws[r = (r + step) & m] != null) {
                         if (++probes >= n) {
                             workQueues = ws = Arrays.copyOf(ws, n <<= 1);
@@ -1456,46 +1356,41 @@
                     }
                 }
                 w.eventCount = w.poolIndex = r;     // establish before recording
-                ws[r] = w;                          // also update seq
-                runState = ((rs = runState) & SHUTDOWN) | ((rs + 2) & ~SHUTDOWN);
+                ws[r] = w;
             }
         } finally {
-            if (!U.compareAndSwapInt(this, MAINLOCK, 1, 0)) {
-                mainLock = 0;
-                synchronized (this) { notifyAll(); };
-            }
+            if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+                releasePlock(nps);
         }
-
     }
 
     /**
      * Final callback from terminating worker, as well as upon failure
-     * to construct or start a worker in addWorker.  Removes record of
-     * worker from array, and adjusts counts. If pool is shutting
-     * down, tries to complete termination.
+     * to construct or start a worker.  Removes record of worker from
+     * array, and adjusts counts. If pool is shutting down, tries to
+     * complete termination.
      *
-     * @param wt the worker thread or null if addWorker 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) {
-            w.runState = -1;                // ensure runState is set
-            long steals = w.totalSteals + w.nsteals, sc;
-            do {} while(!U.compareAndSwapLong(this, STEALCOUNT,
-                                              sc = stealCount, sc + steals));
-            int idx = w.poolIndex;
-            while (!U.compareAndSwapInt(this, MAINLOCK, 0, 1))
-                tryAwaitMainLock();
+            int ps;
+            collectStealCount(w);
+            w.qlock = -1;                // ensure set
+            if (((ps = plock) & PL_LOCK) != 0 ||
+                !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+                ps = acquirePlock();
+            int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
             try {
+                int idx = w.poolIndex;
                 WorkQueue[] ws = workQueues;
                 if (ws != null && idx >= 0 && idx < ws.length && ws[idx] == w)
                     ws[idx] = null;
             } finally {
-                if (!U.compareAndSwapInt(this, MAINLOCK, 1, 0)) {
-                    mainLock = 0;
-                    synchronized (this) { notifyAll(); };
-                }
+                if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+                    releasePlock(nps);
             }
         }
 
@@ -1508,13 +1403,30 @@
         if (!tryTerminate(false, false) && w != null) {
             w.cancelAll();                  // cancel remaining tasks
             if (w.array != null)            // suppress signal if never ran
-                signalWork();               // wake up or create replacement
+                signalWork(null, 1);        // wake up or create replacement
             if (ex == null)                 // help clean refs on way out
                 ForkJoinTask.helpExpungeStaleExceptions();
         }
 
         if (ex != null)                     // rethrow
-            U.throwException(ex);
+            ForkJoinTask.rethrow(ex);
+    }
+
+    /**
+     * Collect worker steal count into total. Called on termination
+     * and upon int overflow of local count. (There is a possible race
+     * in the latter case vs any caller of getStealCount, which can
+     * make its results less accurate than usual.)
+     */
+    final void collectStealCount(WorkQueue w) {
+        if (w != null) {
+            long sc;
+            int ns = w.nsteals;
+            w.nsteals = 0; // handle overflow
+            long steals = (ns >= 0) ? ns : 1L + (long)(Integer.MAX_VALUE);
+            do {} while (!U.compareAndSwapLong(this, STEALCOUNT,
+                                               sc = stealCount, sc + steals));
+        }
     }
 
     // Submissions
@@ -1522,137 +1434,96 @@
     /**
      * Unless shutting down, adds the given task to a submission queue
      * at submitter's current queue index (modulo submission
-     * range). If no queue exists at the index, one is created.  If
-     * the queue is busy, another index is randomly chosen. The
-     * submitMask bounds the effective number of queues to the
-     * (nearest power of two for) parallelism level.
+     * range). Only the most common path is directly handled in this
+     * method. All others are relayed to fullExternalPush.
      *
      * @param task the task. Caller must ensure non-null.
      */
-    private void doSubmit(ForkJoinTask<?> task) {
-        Submitter s = submitters.get();
-        for (int r = s.seed, m = submitMask;;) {
-            WorkQueue[] ws; WorkQueue q;
-            int k = r & m & SQMASK;          // use only even indices
-            if (runState < 0)
-                throw new RejectedExecutionException(); // shutting down
-            else if ((ws = workQueues) == null || ws.length <= k) {
-                while (!U.compareAndSwapInt(this, MAINLOCK, 0, 1))
-                    tryAwaitMainLock();
-                try {
-                    if (workQueues == null)
-                        workQueues = new WorkQueue[submitMask + 1];
-                } finally {
-                    if (!U.compareAndSwapInt(this, MAINLOCK, 1, 0)) {
-                        mainLock = 0;
-                        synchronized (this) { notifyAll(); };
-                    }
-                }
-            }
-            else if ((q = ws[k]) == null) {  // create new queue
-                WorkQueue nq = new WorkQueue(this, null, SHARED_QUEUE);
-                while (!U.compareAndSwapInt(this, MAINLOCK, 0, 1))
-                    tryAwaitMainLock();
-                try {
-                    int rs = runState;       // to update seq
-                    if (ws == workQueues && ws[k] == null) {
-                        ws[k] = nq;
-                        runState = ((rs & SHUTDOWN) | ((rs + 2) & ~SHUTDOWN));
-                    }
-                } finally {
-                    if (!U.compareAndSwapInt(this, MAINLOCK, 1, 0)) {
-                        mainLock = 0;
-                        synchronized (this) { notifyAll(); };
-                    }
-                }
-            }
-            else if (q.trySharedPush(task)) {
-                signalWork();
+    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 &&
+            U.compareAndSwapInt(q, QLOCK, 0, 1)) { // lock
+            int s = q.top, n;
+            if ((a = q.array) != null && a.length > (n = s + 1 - q.base)) {
+                U.putObject(a, (long)(((a.length - 1) & s) << ASHIFT) + ABASE,
+                            task);
+                q.top = s + 1;                     // push on to deque
+                q.qlock = 0;
+                if (n <= 1)
+                    signalWork(q, 1);
                 return;
             }
-            else if (m > 1) {                // move to a different index
-                r ^= r << 13;                // same xorshift as WorkQueues
-                r ^= r >>> 17;
-                s.seed = r ^= r << 5;
-            }
-            else
-                Thread.yield();              // yield if no alternatives
+            q.qlock = 0;
         }
+        fullExternalPush(task);
     }
 
     /**
-     * Submits the given (non-null) task to the common pool, if possible.
+     * 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: creating
+     * workQueue array and setting plock to a valid value. 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 lock bodies must be
+     * exception-free (so no try/finally) so we optimistically
+     * allocate new queues/arrays outside the locks and throw them
+     * away if (very rarely) not needed. Note that the plock seq value
+     * can eventually wrap around zero, but if so harmlessly fails to
+     * reinitialize.
      */
-    static void submitToCommonPool(ForkJoinTask<?> task) {
-        ForkJoinPool p;
-        if ((p = commonPool) == null)
-            throw new RejectedExecutionException("Common Pool Unavailable");
-        p.doSubmit(task);
+    private void fullExternalPush(ForkJoinTask<?> task) {
+        for (Submitter z = null;;) {
+            WorkQueue[] ws; WorkQueue q; int ps, m, r, s;
+            if ((ps = plock) < 0)
+                throw new RejectedExecutionException();
+            else if ((ws = workQueues) == null || (m = ws.length - 1) < 0) {
+                int n = parallelism - 1; n |= n >>> 1; n |= n >>> 2;
+                n |= n >>> 4; n |= n >>> 8; n |= n >>> 16;
+                WorkQueue[] nws = new WorkQueue[(n + 1) << 1]; // power of two
+                if ((ps & PL_LOCK) != 0 ||
+                    !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+                    ps = acquirePlock();
+                if ((ws = workQueues) == null)
+                    workQueues = nws;
+                int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
+                if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+                    releasePlock(nps);
+            }
+            else if (z == null && (z = submitters.get()) == null) {
+                if (U.compareAndSwapInt(this, INDEXSEED,
+                                        s = indexSeed, s += SEED_INCREMENT) &&
+                    s != 0) // skip 0
+                    submitters.set(z = new Submitter(s));
+            }
+            else {
+                int k = (r = z.seed) & m & SQMASK;
+                if ((q = ws[k]) == null && (ps & PL_LOCK) == 0) {
+                    (q = new WorkQueue(this, null, SHARED_QUEUE)).poolIndex = k;
+                    if (((ps = plock) & PL_LOCK) != 0 ||
+                        !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+                        ps = acquirePlock();
+                    WorkQueue w = null;
+                    if ((ws = workQueues) != null && k < ws.length &&
+                        (w = ws[k]) == null)
+                        ws[k] = q;
+                    else
+                        q = w;
+                    int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
+                    if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+                        releasePlock(nps);
+                }
+                if (q != null && q.qlock == 0 && q.fullPush(task, false))
+                    return;
+                r ^= r << 13;                // same xorshift as WorkQueues
+                r ^= r >>> 17;
+                z.seed = r ^= r << 5;        // move to a different index
+            }
+        }
     }
 
-    /**
-     * Returns true if caller is (or may be) submitter to the common
-     * pool, and not all workers are active, and there appear to be
-     * tasks in the associated submission queue.
-     */
-    static boolean canHelpCommonPool() {
-        ForkJoinPool p; WorkQueue[] ws; WorkQueue q;
-        int k = submitters.get().seed & SQMASK;
-        return ((p = commonPool) != null &&
-                (int)(p.ctl >> AC_SHIFT) < 0 &&
-                (ws = p.workQueues) != null &&
-                ws.length > (k &= p.submitMask) &&
-                (q = ws[k]) != null &&
-                q.top - q.base > 0);
-    }
-
-    /**
-     * Returns true if the given task was submitted to common pool
-     * and has not yet commenced execution, and is available for
-     * removal according to execution policies; if so removing the
-     * submission from the pool.
-     *
-     * @param task the task
-     * @return true if successful
-     */
-    static boolean tryUnsubmitFromCommonPool(ForkJoinTask<?> task) {
-        // Peek, looking for task and eligibility before
-        // using trySharedUnpush to actually take it under lock
-        ForkJoinPool p; WorkQueue[] ws; WorkQueue q;
-        ForkJoinTask<?>[] a; int s;
-        int k = submitters.get().seed & SQMASK;
-        return ((p = commonPool) != null &&
-                (int)(p.ctl >> AC_SHIFT) < 0 &&
-                (ws = p.workQueues) != null &&
-                ws.length > (k &= p.submitMask) &&
-                (q = ws[k]) != null &&
-                (a = q.array) != null &&
-                (s = q.top - 1) - q.base >= 0 &&
-                s >= 0 && s < a.length &&
-                a[s] == task &&
-                q.trySharedUnpush(task));
-    }
-
-    /**
-     * Tries to pop a task from common pool with given root
-     */
-    static ForkJoinTask<?> popCCFromCommonPool(CountedCompleter<?> root) {
-        ForkJoinPool p; WorkQueue[] ws; WorkQueue q;
-        ForkJoinTask<?> t;
-        int k = submitters.get().seed & SQMASK;
-        if (root != null &&
-            (p = commonPool) != null &&
-            (int)(p.ctl >> AC_SHIFT) < 0 &&
-            (ws = p.workQueues) != null &&
-            ws.length > (k &= p.submitMask) &&
-            (q = ws[k]) != null && q.top - q.base > 0 &&
-            root.status < 0 &&
-            (t = q.sharedPopCC(root)) != null)
-            return t;
-        return null;
-    }
-
-
     // Maintaining ctl counts
 
     /**
@@ -1664,32 +1535,56 @@
     }
 
     /**
-     * Tries to create one or activate one or more workers if too few are active.
+     * Tries to create (at most one) or activate (possibly several)
+     * workers if too few are active. On contention failure, continues
+     * until at least one worker is signalled or the given queue is
+     * empty or all workers are active.
+     *
+     * @param q if non-null, the queue holding tasks to be signalled
+     * @param signals the target number of signals.
      */
-    final void signalWork() {
-        long c; int u;
-        while ((u = (int)((c = ctl) >>> 32)) < 0) {     // too few active
-            WorkQueue[] ws = workQueues; int e, i; WorkQueue w; Thread p;
-            if ((e = (int)c) > 0) {                     // at least one waiting
-                if (ws != null && (i = e & SMASK) < ws.length &&
+    final void signalWork(WorkQueue q, int signals) {
+        long c; int e, u, i; 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.eventCount = (e + E_SEQ) & E_MASK;
                         if ((p = w.parker) != null)
-                            U.unpark(p);                // activate and release
+                            U.unpark(p);
+                        if (--signals <= 0)
+                            break;
+                    }
+                    else
+                        signals = 1;
+                    if ((q != null && q.queueSize() == 0))
                         break;
-                    }
                 }
                 else
                     break;
             }
-            else if (e == 0 && (u & SHORT_SIGN) != 0) { // too few total
+            else if (e == 0 && (u & SHORT_SIGN) != 0) {
                 long nc = (long)(((u + UTC_UNIT) & UTC_MASK) |
                                  ((u + UAC_UNIT) & UAC_MASK)) << 32;
                 if (U.compareAndSwapLong(this, CTL, c, nc)) {
-                    addWorker();
+                    ForkJoinWorkerThread wt = null;
+                    Throwable ex = null;
+                    boolean started = false;
+                    try {
+                        ForkJoinWorkerThreadFactory fac;
+                        if ((fac = factory) != null &&
+                            (wt = fac.newThread(this)) != null) {
+                            wt.start();
+                            started = true;
+                        }
+                    } catch (Throwable rex) {
+                        ex = rex;
+                    }
+                    if (!started)
+                        deregisterWorker(wt, ex); // adjust counts on failure
                     break;
                 }
             }
@@ -1704,8 +1599,9 @@
      * Top-level runloop for workers, called by ForkJoinWorkerThread.run.
      */
     final void runWorker(WorkQueue w) {
-        w.growArray(false);         // initialize queue array in this thread
-        do { w.runTask(scan(w)); } while (w.runState >= 0);
+        // initialize queue array in this thread
+        w.array = new ForkJoinTask<?>[WorkQueue.INITIAL_QUEUE_CAPACITY];
+        do { w.runTask(scan(w)); } while (w.qlock >= 0);
     }
 
     /**
@@ -1721,108 +1617,80 @@
      * relative prime, checking each at least once).  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.  On failure to find a task, we
+     * 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 trtry 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 a complete sweep, try to release a waiting worker.  If
-     * the scan terminated because the worker is inactivated, then the
-     * released worker will often be the calling worker, and it can
-     * succeed obtaining a task on the next call. Or maybe it is
-     * another worker, but with same net effect. Releasing in other
-     * cases as well ensures that we have enough workers running.
-     *
      * * 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 check for
      * termination and possibly shrink pool.
      *
-     * * If already inactive, and the caller has run a task since the
-     * last empty scan, return (to allow rescan) unless others are
-     * also inactivated.  Field WorkQueue.rescans counts down on each
-     * scan to ensure eventual inactivation and blocking.
-     *
-     * * If already enqueued and none of the above apply, park
-     * awaiting signal,
+     * * If already enqueued and none of the above apply, possibly
+     * (with 1/2 probablility) park awaiting signal, else lingering to
+     * help scan and signal.
      *
      * @param w the worker (via its WorkQueue)
      * @return a task or null if none found
      */
     private final ForkJoinTask<?> scan(WorkQueue w) {
-        WorkQueue[] ws;                       // first update random seed
+        WorkQueue[] ws; WorkQueue q;           // first update random seed
         int r = w.seed; r ^= r << 13; r ^= r >>> 17; w.seed = r ^= r << 5;
-        int rs = runState, m;                 // volatile read order matters
+        int ps = plock, m;                     // volatile read order matters
         if ((ws = workQueues) != null && (m = ws.length - 1) > 0) {
-            int ec = w.eventCount;            // ec is negative if inactive
-            int step = (r >>> 16) | 1;        // relative prime
-            for (int j = (m + 1) << 2; ; r += step) {
-                WorkQueue q; ForkJoinTask<?> t; ForkJoinTask<?>[] a; int b;
+            int ec = w.eventCount;             // ec is negative if inactive
+            int step = (r >>> 16) | 1;         // relatively prime
+            for (int j = (m + 1) << 2;  ; --j, r += step) {
+                ForkJoinTask<?> t; ForkJoinTask<?>[] a; int b, n;
                 if ((q = ws[r & m]) != null && (b = q.base) - q.top < 0 &&
-                    (a = q.array) != null) {  // probably nonempty
+                    (a = q.array) != null) {   // probably nonempty
                     int i = (((a.length - 1) & b) << ASHIFT) + ABASE;
                     t = (ForkJoinTask<?>)U.getObjectVolatile(a, i);
                     if (q.base == b && ec >= 0 && t != null &&
                         U.compareAndSwapObject(a, i, t, null)) {
-                        if (q.top - (q.base = b + 1) > 0)
-                            signalWork();    // help pushes signal
-                        return t;
+                        if ((n = q.top - (q.base = b + 1)) > 0)
+                            signalWork(q, n);
+                        return t;              // taken
                     }
-                    else if (ec < 0 || j <= m) {
-                        rs = 0;               // mark scan as imcomplete
-                        break;                // caller can retry after release
+                    if (j < m || (ec < 0 && (ec = w.eventCount) < 0)) {
+                        if ((n = q.queueSize() - 1) > 0)
+                            signalWork(q, n);
+                        break;                 // let caller retry after signal
                     }
                 }
-                if (--j < 0)
+                else if (j < 0) {              // end of scan
+                    long c = ctl; int e;
+                    if (plock != ps)           // incomplete sweep
+                        break;
+                    if ((e = (int)c) < 0)      // pool is terminating
+                        w.qlock = -1;
+                    else if (ec >= 0) {        // try to enqueue/inactivate
+                        long nc = ((long)ec |
+                                   ((c - AC_UNIT) & (AC_MASK|TC_MASK)));
+                        w.nextWait = e;
+                        w.eventCount = ec | INT_SIGN; // mark as inactive
+                        if (ctl != c ||
+                            !U.compareAndSwapLong(this, CTL, c, nc))
+                            w.eventCount = ec; // unmark on CAS failure
+                        else if ((int)(c >> AC_SHIFT) == 1 - parallelism)
+                            idleAwaitWork(w, nc, c);  // quiescent
+                    }
+                    else if (w.seed >= 0 && w.eventCount < 0) {
+                        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);
+                    }
                     break;
-            }
-
-            long c = ctl; int e = (int)c, a = (int)(c >> AC_SHIFT), nr, ns;
-            if (e < 0)                        // decode ctl on empty scan
-                w.runState = -1;              // pool is terminating
-            else if (rs == 0 || rs != runState) { // incomplete scan
-                WorkQueue v; Thread p;        // try to release a waiter
-                if (e > 0 && a < 0 && w.eventCount == ec &&
-                    (v = ws[e & m]) != null && v.eventCount == (e | INT_SIGN)) {
-                    long nc = ((long)(v.nextWait & E_MASK) |
-                               ((c + AC_UNIT) & (AC_MASK|TC_MASK)));
-                    if (ctl == c && U.compareAndSwapLong(this, CTL, c, nc)) {
-                        v.eventCount = (e + E_SEQ) & E_MASK;
-                        if ((p = v.parker) != null)
-                            U.unpark(p);
-                    }
-                }
-            }
-            else if (ec >= 0) {               // try to enqueue/inactivate
-                long nc = (long)ec | ((c - AC_UNIT) & (AC_MASK|TC_MASK));
-                w.nextWait = e;
-                w.eventCount = ec | INT_SIGN; // mark as inactive
-                if (ctl != c || !U.compareAndSwapLong(this, CTL, c, nc))
-                    w.eventCount = ec;        // unmark on CAS failure
-                else {
-                    if ((ns = w.nsteals) != 0) {
-                        w.nsteals = 0;        // set rescans if ran task
-                        w.rescans = (a > 0) ? 0 : a + parallelism;
-                        w.totalSteals += ns;
-                    }
-                    if (a == 1 - parallelism) // quiescent
-                        idleAwaitWork(w, nc, c);
-                }
-            }
-            else if (w.eventCount < 0) {      // already queued
-                int ac = a + parallelism;
-                if ((nr = w.rescans) > 0)     // continue rescanning
-                    w.rescans = (ac < nr) ? ac : nr - 1;
-                else if (((w.seed >>> 16) & ac) == 0) { // randomize park
-                    Thread.interrupted();     // clear status
-                    Thread wt = Thread.currentThread();
-                    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);
                 }
             }
         }
@@ -1842,8 +1710,9 @@
      * @param prevCtl the ctl value to restore if thread is terminated
      */
     private void idleAwaitWork(WorkQueue w, long currentCtl, long prevCtl) {
-        if (w.eventCount < 0 && !tryTerminate(false, false) &&
-            (int)prevCtl != 0 && !hasQueuedSubmissions() && ctl == currentCtl) {
+        if (w.eventCount < 0 &&
+            (this == commonPool || !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 - 100000L; // 1ms slop
@@ -1861,7 +1730,7 @@
                 if (deadline - System.nanoTime() <= 0L &&
                     U.compareAndSwapLong(this, CTL, currentCtl, prevCtl)) {
                     w.eventCount = (w.eventCount + E_SEQ) | E_MASK;
-                    w.runState = -1;   // shrink
+                    w.qlock = -1;   // shrink
                     break;
                 }
             }
@@ -1869,6 +1738,31 @@
     }
 
     /**
+     * Scans through queues looking for work while joining a task;
+     * if any are present, signals.
+     *
+     * @param task to return early if done
+     * @param origin an index to start scan
+     */
+    final int helpSignal(ForkJoinTask<?> task, int origin) {
+        WorkQueue[] ws; WorkQueue q; int m, n, s;
+        if (task != null && (ws = workQueues) != null &&
+            (m = ws.length - 1) >= 0) {
+            for (int i = 0; i <= m; ++i) {
+                if ((s = task.status) < 0)
+                    return s;
+                if ((q = ws[(i + origin) & m]) != null &&
+                    (n = q.queueSize()) > 0) {
+                    signalWork(q, n);
+                    if ((int)(ctl >> AC_SHIFT) >= 0)
+                        break;
+                }
+            }
+        }
+        return 0;
+    }
+
+    /**
      * Tries to locate and execute tasks for a stealer of the given
      * task, or in turn one of its stealers, Traces currentSteal ->
      * currentJoin links looking for a thread working on a descendant
@@ -1955,88 +1849,77 @@
     }
 
     /**
-     * If task is at base of some steal queue, steals and executes it.
+     * Analog of tryHelpStealer for CountedCompleters. Tries to steal
+     * and run tasks within the target's computation
      *
-     * @param joiner the joining worker
-     * @param task the task
+     * @param task the task to join
+     * @param mode if shared, exit upon completing any task
+     * if all workers are active
+     *
      */
-    private void tryPollForAndExec(WorkQueue joiner, ForkJoinTask<?> task) {
-        WorkQueue[] ws;
-        if ((ws = workQueues) != null) {
-            for (int j = 1; j < ws.length && task.status >= 0; j += 2) {
-                WorkQueue q = ws[j];
-                if (q != null && q.pollFor(task)) {
-                    joiner.runSubtask(task);
+    private int helpComplete(ForkJoinTask<?> task, int mode) {
+        WorkQueue[] ws; WorkQueue q; int m, n, s;
+        if (task != null && (ws = workQueues) != null &&
+            (m = ws.length - 1) >= 0) {
+            for (int j = 1, origin = j;;) {
+                if ((s = task.status) < 0)
+                    return s;
+                if ((q = ws[j & m]) != null && q.pollAndExecCC(task)) {
+                    origin = j;
+                    if (mode == SHARED_QUEUE && (int)(ctl >> AC_SHIFT) >= 0)
+                        break;
+                }
+                else if ((j = (j + 2) & m) == origin)
                     break;
-                }
             }
         }
+        return 0;
     }
 
     /**
      * Tries to decrement active count (sometimes implicitly) and
      * possibly release or create a compensating worker in preparation
      * for blocking. Fails on contention or termination. Otherwise,
-     * adds a new thread if no idle workers are available and either
-     * pool would become completely starved or: (at least half
-     * starved, and fewer than 50% spares exist, and there is at least
-     * one task apparently available). Even though the availability
-     * check requires a full scan, it is worthwhile in reducing false
-     * alarms.
-     *
-     * @param task if non-null, a task being waited for
-     * @param blocker if non-null, a blocker being waited for
-     * @return true if the caller can block, else should recheck and retry
+     * adds a new thread if no idle workers are available and pool
+     * may become starved.
      */
-    final boolean tryCompensate(ForkJoinTask<?> task, ManagedBlocker blocker) {
-        int pc = parallelism, e;
-        long c = ctl;
-        WorkQueue[] ws = workQueues;
-        if ((e = (int)c) >= 0 && ws != null) {
-            int u, a, ac, hc;
-            int tc = (short)((u = (int)(c >>> 32)) >>> UTC_SHIFT) + pc;
-            boolean replace = false;
-            if ((a = u >> UAC_SHIFT) <= 0) {
-                if ((ac = a + pc) <= 1)
-                    replace = true;
-                else if ((e > 0 || (task != null &&
-                                    ac <= (hc = pc >>> 1) && tc < pc + hc))) {
-                    WorkQueue w;
-                    for (int j = 0; j < ws.length; ++j) {
-                        if ((w = ws[j]) != null && !w.isEmpty()) {
-                            replace = true;
-                            break;   // in compensation range and tasks available
-                        }
-                    }
+    final boolean tryCompensate() {
+        int pc = parallelism, e, u, i, tc; long c;
+        WorkQueue[] ws; WorkQueue w; Thread p;
+        if ((e = (int)(c = ctl)) >= 0 && (ws = workQueues) != null) {
+            if (e != 0 && (i = e & SMASK) < ws.length &&
+                (w = ws[i]) != null && w.eventCount == (e | INT_SIGN)) {
+                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;
+                    if ((p = w.parker) != null)
+                        U.unpark(p);
+                    return true;   // replace with idle worker
                 }
             }
-            if ((task == null || task.status >= 0) && // recheck need to block
-                (blocker == null || !blocker.isReleasable()) && ctl == c) {
-                if (!replace) {          // no compensation
-                    long nc = ((c - AC_UNIT) & AC_MASK) | (c & ~AC_MASK);
-                    if (U.compareAndSwapLong(this, CTL, c, nc))
-                        return true;
-                }
-                else if (e != 0) {       // release an idle worker
-                    WorkQueue w; Thread p; int i;
-                    if ((i = e & SMASK) < ws.length && (w = ws[i]) != null) {
-                        long nc = ((long)(w.nextWait & E_MASK) |
-                                   (c & (AC_MASK|TC_MASK)));
-                        if (w.eventCount == (e | INT_SIGN) &&
-                            U.compareAndSwapLong(this, CTL, c, nc)) {
-                            w.eventCount = (e + E_SEQ) & E_MASK;
-                            if ((p = w.parker) != null)
-                                U.unpark(p);
+            else if ((short)((u = (int)(c >>> 32)) >>> UTC_SHIFT) >= 0 &&
+                     (u >> UAC_SHIFT) + pc > 1) {
+                long nc = ((c - AC_UNIT) & AC_MASK) | (c & ~AC_MASK);
+                if (U.compareAndSwapLong(this, CTL, c, nc))
+                    return true;    // no compensation
+            }
+            else if ((tc = u + pc) < MAX_CAP) {
+                long nc = ((c + TC_UNIT) & TC_MASK) | (c & ~TC_MASK);
+                if (U.compareAndSwapLong(this, CTL, c, nc)) {
+                    Throwable ex = null;
+                    ForkJoinWorkerThread wt = null;
+                    try {
+                        ForkJoinWorkerThreadFactory fac;
+                        if ((fac = factory) != null &&
+                            (wt = fac.newThread(this)) != null) {
+                            wt.start();
                             return true;
                         }
+                    } catch (Throwable rex) {
+                        ex = rex;
                     }
-                }
-                else if (tc < MAX_CAP) { // create replacement
-                    long nc = ((c + TC_UNIT) & TC_MASK) | (c & ~TC_MASK);
-                    if (U.compareAndSwapLong(this, CTL, c, nc)) {
-                        addWorker();
-                        return true;
-                    }
+                    deregisterWorker(wt, ex); // adjust counts etc
                 }
             }
         }
@@ -2051,48 +1934,39 @@
      * @return task status on exit
      */
     final int awaitJoin(WorkQueue joiner, ForkJoinTask<?> task) {
-        int s;
-        if ((s = task.status) >= 0) {
+        int s = 0;
+        if (joiner != null && task != null && (s = task.status) >= 0) {
             ForkJoinTask<?> prevJoin = joiner.currentJoin;
             joiner.currentJoin = task;
-            long startTime = 0L;
-            for (int k = 0;;) {
-                if ((s = (joiner.isEmpty() ?           // try to help
-                          tryHelpStealer(joiner, task) :
-                          joiner.tryRemoveAndExec(task))) == 0 &&
-                    (s = task.status) >= 0) {
-                    if (k == 0) {
-                        startTime = System.nanoTime();
-                        tryPollForAndExec(joiner, task); // check uncommon case
+            do {} while ((s = task.status) >= 0 &&
+                         joiner.queueSize() > 0 &&
+                         joiner.tryRemoveAndExec(task)); // process local tasks
+            if (s >= 0 && (s = task.status) >= 0 &&
+                (s = helpSignal(task, joiner.poolIndex)) >= 0 &&
+                (task instanceof CountedCompleter))
+                s = helpComplete(task, LIFO_QUEUE);
+            while (s >= 0 && (s = task.status) >= 0) {
+                if ((joiner.queueSize() > 0 ||           // try helping
+                     (s = tryHelpStealer(joiner, task)) == 0) &&
+                    (s = task.status) >= 0 && tryCompensate()) {
+                    if (task.trySetSignal() && (s = task.status) >= 0) {
+                        synchronized (task) {
+                            if (task.status >= 0) {
+                                try {                // see ForkJoinTask
+                                    task.wait();     //  for explanation
+                                } catch (InterruptedException ie) {
+                                }
+                            }
+                            else
+                                task.notifyAll();
+                        }
                     }
-                    else if ((k & (MAX_HELP - 1)) == 0 &&
-                             System.nanoTime() - startTime >=
-                             COMPENSATION_DELAY &&
-                             tryCompensate(task, null)) {
-                        if (task.trySetSignal()) {
-                            synchronized (task) {
-                                if (task.status >= 0) {
-                                    try {                // see ForkJoinTask
-                                        task.wait();     //  for explanation
-                                    } catch (InterruptedException ie) {
-                                    }
-                                }
-                                else
-                                    task.notifyAll();
-                            }
-                        }
-                        long c;                          // re-activate
-                        do {} while (!U.compareAndSwapLong
-                                     (this, CTL, c = ctl, c + AC_UNIT));
-                    }
+                    long c;                          // re-activate
+                    do {} while (!U.compareAndSwapLong
+                                 (this, CTL, c = ctl, c + AC_UNIT));
                 }
-                if (s < 0 || (s = task.status) < 0) {
-                    joiner.currentJoin = prevJoin;
-                    break;
-                }
-                else if ((k++ & (MAX_HELP - 1)) == MAX_HELP >>> 1)
-                    Thread.yield();                     // for politeness
             }
+            joiner.currentJoin = prevJoin;
         }
         return s;
     }
@@ -2104,16 +1978,25 @@
      *
      * @param joiner the joining worker
      * @param task the task
-     * @return task status on exit
      */
-    final int helpJoinOnce(WorkQueue joiner, ForkJoinTask<?> task) {
+    final void helpJoinOnce(WorkQueue joiner, ForkJoinTask<?> task) {
         int s;
-        while ((s = task.status) >= 0 &&
-               (joiner.isEmpty() ?
-                tryHelpStealer(joiner, task) :
-                joiner.tryRemoveAndExec(task)) != 0)
-            ;
-        return s;
+        if (joiner != null && task != null && (s = task.status) >= 0) {
+            ForkJoinTask<?> prevJoin = joiner.currentJoin;
+            joiner.currentJoin = task;
+            do {} while ((s = task.status) >= 0 &&
+                         joiner.queueSize() > 0 &&
+                         joiner.tryRemoveAndExec(task));
+            if (s >= 0 && (s = task.status) >= 0 &&
+                (s = helpSignal(task, joiner.poolIndex)) >= 0 &&
+                (task instanceof CountedCompleter))
+                s = helpComplete(task, LIFO_QUEUE);
+            if (s >= 0 && joiner.queueSize() == 0) {
+                do {} while (task.status >= 0 &&
+                             tryHelpStealer(joiner, task) > 0);
+            }
+            joiner.currentJoin = prevJoin;
+        }
     }
 
     /**
@@ -2121,21 +2004,20 @@
      * 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
      */
-    private WorkQueue findNonEmptyStealQueue(WorkQueue w) {
-        // Similar to loop in scan(), but ignoring submissions
-        int r = w.seed; r ^= r << 13; r ^= r >>> 17; w.seed = r ^= r << 5;
+    private WorkQueue findNonEmptyStealQueue(int r) {
         int step = (r >>> 16) | 1;
         for (WorkQueue[] ws;;) {
-            int rs = runState, m;
+            int ps = plock, m;
             if ((ws = workQueues) == null || (m = ws.length - 1) < 1)
                 return null;
             for (int j = (m + 1) << 2; ; r += step) {
                 WorkQueue q = ws[((r << 1) | 1) & m];
-                if (q != null && !q.isEmpty())
+                if (q != null && q.queueSize() > 0)
                     return q;
                 else if (--j < 0) {
-                    if (runState == rs)
+                    if (plock == ps)
                         return null;
                     break;
                 }
@@ -2154,7 +2036,8 @@
             ForkJoinTask<?> localTask; // exhaust local queue
             while ((localTask = w.nextLocalTask()) != null)
                 localTask.doExec();
-            WorkQueue q = findNonEmptyStealQueue(w);
+            // Similar to loop in scan(), but ignoring submissions
+            WorkQueue q = findNonEmptyStealQueue(w.nextSeed());
             if (q != null) {
                 ForkJoinTask<?> t; int b;
                 if (!active) {      // re-establish active count
@@ -2185,31 +2068,6 @@
     }
 
     /**
-     * Restricted version of helpQuiescePool for non-FJ callers
-     */
-    static void externalHelpQuiescePool() {
-        ForkJoinPool p; WorkQueue[] ws; WorkQueue q, sq;
-        ForkJoinTask<?>[] a; int b;
-        ForkJoinTask<?> t = null;
-        int k = submitters.get().seed & SQMASK;
-        if ((p = commonPool) != null &&
-            (int)(p.ctl >> AC_SHIFT) < 0 &&
-            (ws = p.workQueues) != null &&
-            ws.length > (k &= p.submitMask) &&
-            (q = ws[k]) != null) {
-            while (q.top - q.base > 0) {
-                if ((t = q.sharedPop()) != null)
-                    break;
-            }
-            if (t == null && (sq = p.findNonEmptyStealQueue(q)) != null &&
-                (b = sq.base) - sq.top < 0)
-                t = sq.pollAt(b);
-            if (t != null)
-                t.doExec();
-        }
-    }
-
-    /**
      * Gets and removes a local or stolen task for the given worker.
      *
      * @return a task, if available
@@ -2219,7 +2077,7 @@
             WorkQueue q; int b;
             if ((t = w.nextLocalTask()) != null)
                 return t;
-            if ((q = findNonEmptyStealQueue(w)) == null)
+            if ((q = findNonEmptyStealQueue(w.nextSeed())) == null)
                 return null;
             if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null)
                 return t;
@@ -2227,33 +2085,64 @@
     }
 
     /**
-     * Returns the approximate (non-atomic) number of idle threads per
-     * active thread to offset steal queue size for method
-     * ForkJoinTask.getSurplusQueuedTaskCount().
+     * Returns a cheap heuristic guide for task partitioning when
+     * programmers, frameworks, tools, or languages have little or no
+     * idea about task granularity.  In essence by offering this
+     * method, we ask users only about tradeoffs in overhead vs
+     * expected throughput and its variance, rather than how finely to
+     * partition tasks.
+     *
+     * In a steady state strict (tree-structured) computation, each
+     * thread makes available for stealing enough tasks for other
+     * threads to remain active. Inductively, if all threads play by
+     * the same rules, each thread should make available only a
+     * constant number of tasks.
+     *
+     * The minimum useful constant is just 1. But using a value of 1
+     * would require immediate replenishment upon each steal to
+     * maintain enough tasks, which is infeasible.  Further,
+     * partitionings/granularities of offered tasks should minimize
+     * steal rates, which in general means that threads nearer the top
+     * of computation tree should generate more than those nearer the
+     * bottom. In perfect steady state, each thread is at
+     * approximately the same level of computation tree. However,
+     * producing extra tasks amortizes the uncertainty of progress and
+     * diffusion assumptions.
+     *
+     * So, users will want to use values larger, but not much larger
+     * than 1 to both smooth over transient shortages and hedge
+     * against uneven progress; as traded off against the cost of
+     * extra task overhead. We leave the user to pick a threshold
+     * value to compare with the results of this call to guide
+     * decisions, but recommend values such as 3.
+     *
+     * When all threads are active, it is on average OK to estimate
+     * surplus strictly locally. In steady-state, if one thread is
+     * maintaining say 2 surplus tasks, then so are others. So we can
+     * just use estimated queue length.  However, this strategy alone
+     * leads to serious mis-estimates in some non-steady-state
+     * conditions (ramp-up, ramp-down, other stalls). We can detect
+     * many of these by further considering the number of "idle"
+     * threads, that are known to have zero queued tasks, so
+     * compensate by a factor of (#idle/#active) threads.
+     *
+     * Note: The approximation of #busy workers as #active workers is
+     * not very good under current signalling scheme, and should be
+     * improved.
      */
-    final int idlePerActive() {
-        // Approximate at powers of two for small values, saturate past 4
-        int p = parallelism;
-        int a = p + (int)(ctl >> AC_SHIFT);
-        return (a > (p >>>= 1) ? 0 :
-                a > (p >>>= 1) ? 1 :
-                a > (p >>>= 1) ? 2 :
-                a > (p >>>= 1) ? 4 :
-                8);
-    }
-
-    /**
-     * Returns approximate submission queue length for the given caller
-     */
-    static int getEstimatedSubmitterQueueLength() {
-        ForkJoinPool p; WorkQueue[] ws; WorkQueue q;
-        int k = submitters.get().seed & SQMASK;
-        return ((p = commonPool) != null &&
-                p.runState >= 0 &&
-                (ws = p.workQueues) != null &&
-                ws.length > (k &= p.submitMask) &&
-                (q = ws[k]) != null) ?
-            q.queueSize() : 0;
+    static int getSurplusQueuedTaskCount() {
+        Thread t; ForkJoinWorkerThread wt; ForkJoinPool pool; WorkQueue q;
+        if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)) {
+            int b = (q = (wt = (ForkJoinWorkerThread)t).workQueue).base;
+            int p = (pool = wt.pool).parallelism;
+            int a = (int)(pool.ctl >> AC_SHIFT) + p;
+            return q.top - b - (a > (p >>>= 1) ? 0 :
+                                a > (p >>>= 1) ? 1 :
+                                a > (p >>>= 1) ? 2 :
+                                a > (p >>>= 1) ? 4 :
+                                8);
+        }
+        return 0;
     }
 
     //  Termination
@@ -2273,28 +2162,27 @@
      * @return true if now terminating or terminated
      */
     private boolean tryTerminate(boolean now, boolean enable) {
+        if (this == commonPool)                     // cannot shut down
+            return false;
         for (long c;;) {
             if (((c = ctl) & STOP_BIT) != 0) {      // already terminating
                 if ((short)(c >>> TC_SHIFT) == -parallelism) {
-                    synchronized(this) {
+                    synchronized (this) {
                         notifyAll();                // signal when 0 workers
                     }
                 }
                 return true;
             }
-            if (runState >= 0) {                    // not yet enabled
+            if (plock >= 0) {                       // not yet enabled
+                int ps;
                 if (!enable)
                     return false;
-                while (!U.compareAndSwapInt(this, MAINLOCK, 0, 1))
-                    tryAwaitMainLock();
-                try {
-                    runState |= SHUTDOWN;
-                } finally {
-                    if (!U.compareAndSwapInt(this, MAINLOCK, 1, 0)) {
-                        mainLock = 0;
-                        synchronized (this) { notifyAll(); };
-                    }
-                }
+                if (((ps = plock) & PL_LOCK) != 0 ||
+                    !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+                    ps = acquirePlock();
+                int nps = SHUTDOWN;
+                if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+                    releasePlock(nps);
             }
             if (!now) {                             // check if idle & no tasks
                 if ((int)(c >> AC_SHIFT) != -parallelism ||
@@ -2317,7 +2205,7 @@
                         int n = ws.length;
                         for (int i = 0; i < n; ++i) {
                             if ((w = ws[i]) != null) {
-                                w.runState = -1;
+                                w.qlock = -1;
                                 if (pass > 0) {
                                     w.cancelAll();
                                     if (pass > 1)
@@ -2336,7 +2224,7 @@
                             if (w.eventCount == (e | INT_SIGN) &&
                                 U.compareAndSwapLong(this, CTL, cc, nc)) {
                                 w.eventCount = (e + E_SEQ) & E_MASK;
-                                w.runState = -1;
+                                w.qlock = -1;
                                 if ((p = w.parker) != null)
                                     U.unpark(p);
                             }
@@ -2347,6 +2235,142 @@
         }
     }
 
+    // external operations on common pool
+
+    /**
+     * Returns common pool queue for a thread that has submitted at
+     * least one task.
+     */
+    static WorkQueue commonSubmitterQueue() {
+        ForkJoinPool p; WorkQueue[] ws; int m; Submitter z;
+        return ((z = submitters.get()) != null &&
+                (p = commonPool) != null &&
+                (ws = p.workQueues) != null &&
+                (m = ws.length - 1) >= 0) ?
+            ws[m & z.seed & SQMASK] : null;
+    }
+
+    /**
+     * 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; long j;
+        if ((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 &&
+            U.getObjectVolatile
+            (a, j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE) == 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;
+            }
+            q.qlock = 0;
+        }
+        return false;
+    }
+
+    /**
+     * 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; 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 || (int)(ctl >> AC_SHIFT) >= 0)
+                    break;
+                if (task == null) {
+                    if (helpSignal(root, q.poolIndex) >= 0)
+                        helpComplete(root, SHARED_QUEUE);
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * 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; long j;
+        if (t != null && t.status >= 0 &&
+            (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) {
+            if ((s = q.top) != q.base &&
+                U.getObjectVolatile
+                (a, j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE) == 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;
+        int r = ThreadLocalRandom.current().nextInt();
+        if ((p = commonPool) != null &&
+            (q = p.findNonEmptyStealQueue(r)) != null &&
+            (b = q.base) - q.top < 0 &&
+            (t = q.pollAt(b)) != null)
+            t.doExec();
+    }
+
     // Exported methods
 
     // Constructors
@@ -2424,34 +2448,26 @@
         this.localMode = asyncMode ? FIFO_QUEUE : LIFO_QUEUE;
         long np = (long)(-parallelism); // offset ctl counts
         this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
-        // Use nearest power 2 for workQueues size. See Hackers Delight sec 3.2.
-        int n = parallelism - 1;
-        n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16;
-        this.submitMask = ((n + 1) << 1) - 1;
-        int pn = poolNumberGenerator.incrementAndGet();
+        int pn = nextPoolId();
         StringBuilder sb = new StringBuilder("ForkJoinPool-");
         sb.append(Integer.toString(pn));
         sb.append("-worker-");
         this.workerNamePrefix = sb.toString();
-        this.runState = 1;              // set init flag
     }
 
     /**
      * Constructor for common pool, suitable only for static initialization.
      * Basically the same as above, but uses smallest possible initial footprint.
      */
-    ForkJoinPool(int parallelism, int submitMask,
+    ForkJoinPool(int parallelism, long ctl,
                  ForkJoinWorkerThreadFactory factory,
                  Thread.UncaughtExceptionHandler handler) {
+        this.parallelism = parallelism;
+        this.ctl = ctl;
         this.factory = factory;
         this.ueh = handler;
-        this.submitMask = submitMask;
-        this.parallelism = parallelism;
-        long np = (long)(-parallelism);
-        this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
         this.localMode = LIFO_QUEUE;
         this.workerNamePrefix = "ForkJoinPool.commonPool-worker-";
-        this.runState = 1;
     }
 
     /**
@@ -2460,10 +2476,7 @@
      * @return the common pool instance
      */
     public static ForkJoinPool commonPool() {
-        ForkJoinPool p;
-        if ((p = commonPool) == null)
-            throw new Error("Common Pool Unavailable");
-        return p;
+        return commonPool; // cannot be null (if so, a static init error)
     }
 
     // Execution methods
@@ -2487,7 +2500,7 @@
     public <T> T invoke(ForkJoinTask<T> task) {
         if (task == null)
             throw new NullPointerException();
-        doSubmit(task);
+        externalPush(task);
         return task.join();
     }
 
@@ -2502,7 +2515,7 @@
     public void execute(ForkJoinTask<?> task) {
         if (task == null)
             throw new NullPointerException();
-        doSubmit(task);
+        externalPush(task);
     }
 
     // AbstractExecutorService methods
@@ -2520,7 +2533,7 @@
             job = (ForkJoinTask<?>) task;
         else
             job = new ForkJoinTask.AdaptedRunnableAction(task);
-        doSubmit(job);
+        externalPush(job);
     }
 
     /**
@@ -2535,7 +2548,7 @@
     public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {
         if (task == null)
             throw new NullPointerException();
-        doSubmit(task);
+        externalPush(task);
         return task;
     }
 
@@ -2546,7 +2559,7 @@
      */
     public <T> ForkJoinTask<T> submit(Callable<T> task) {
         ForkJoinTask<T> job = new ForkJoinTask.AdaptedCallable<T>(task);
-        doSubmit(job);
+        externalPush(job);
         return job;
     }
 
@@ -2557,7 +2570,7 @@
      */
     public <T> ForkJoinTask<T> submit(Runnable task, T result) {
         ForkJoinTask<T> job = new ForkJoinTask.AdaptedRunnable<T>(task, result);
-        doSubmit(job);
+        externalPush(job);
         return job;
     }
 
@@ -2574,7 +2587,7 @@
             job = (ForkJoinTask<?>) task;
         else
             job = new ForkJoinTask.AdaptedRunnableAction(task);
-        doSubmit(job);
+        externalPush(job);
         return job;
     }
 
@@ -2596,7 +2609,7 @@
         try {
             for (Callable<T> t : tasks) {
                 ForkJoinTask<T> f = new ForkJoinTask.AdaptedCallable<T>(t);
-                doSubmit(f);
+                externalPush(f);
                 fs.add(f);
             }
             for (ForkJoinTask<T> f : fs)
@@ -2733,7 +2746,7 @@
         if ((ws = workQueues) != null) {
             for (int i = 1; i < ws.length; i += 2) {
                 if ((w = ws[i]) != null)
-                    count += w.totalSteals;
+                    count += w.nsteals;
             }
         }
         return count;
@@ -2790,7 +2803,7 @@
         WorkQueue[] ws; WorkQueue w;
         if ((ws = workQueues) != null) {
             for (int i = 0; i < ws.length; i += 2) {
-                if ((w = ws[i]) != null && !w.isEmpty())
+                if ((w = ws[i]) != null && w.queueSize() != 0)
                     return true;
             }
         }
@@ -2869,7 +2882,7 @@
                         qs += size;
                     else {
                         qt += size;
-                        st += w.totalSteals;
+                        st += w.nsteals;
                         if (w.isApparentlyUnblocked())
                             ++rc;
                     }
@@ -2885,7 +2898,7 @@
         if ((c & STOP_BIT) != 0)
             level = (tc == 0) ? "Terminated" : "Terminating";
         else
-            level = runState < 0 ? "Shutting down" : "Running";
+            level = plock < 0 ? "Shutting down" : "Running";
         return super.toString() +
             "[" + level +
             ", parallelism = " + pc +
@@ -2914,8 +2927,7 @@
      */
     public void shutdown() {
         checkPermission();
-        if (this != commonPool)
-            tryTerminate(false, true);
+        tryTerminate(false, true);
     }
 
     /**
@@ -2938,8 +2950,7 @@
      */
     public List<Runnable> shutdownNow() {
         checkPermission();
-        if (this != commonPool)
-            tryTerminate(true, true);
+        tryTerminate(true, true);
         return Collections.emptyList();
     }
 
@@ -2979,13 +2990,15 @@
      * @return {@code true} if this pool has been shut down
      */
     public boolean isShutdown() {
-        return runState < 0;
+        return plock < 0;
     }
 
     /**
-     * Blocks until all tasks have completed execution after a shutdown
-     * request, or the timeout occurs, or the current thread is
-     * interrupted, whichever happens first.
+     * 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.
      *
      * @param timeout the maximum time to wait
      * @param unit the time unit of the timeout argument
@@ -3000,7 +3013,7 @@
             return true;
         long startTime = System.nanoTime();
         boolean terminated = false;
-        synchronized(this) {
+        synchronized (this) {
             for (long waitTime = nanos, millis = 0L;;) {
                 if (terminated = isTerminated() ||
                     waitTime <= 0L ||
@@ -3109,19 +3122,36 @@
     public static void managedBlock(ManagedBlocker blocker)
         throws InterruptedException {
         Thread t = Thread.currentThread();
-        ForkJoinPool p = ((t instanceof ForkJoinWorkerThread) ?
-                          ((ForkJoinWorkerThread)t).pool : null);
-        while (!blocker.isReleasable()) {
-            if (p == null || p.tryCompensate(null, blocker)) {
-                try {
-                    do {} while (!blocker.isReleasable() && !blocker.block());
-                } finally {
-                    if (p != null)
+        if (t instanceof ForkJoinWorkerThread) {
+            ForkJoinPool p = ((ForkJoinWorkerThread)t).pool;
+            while (!blocker.isReleasable()) { // variant of helpSignal
+                WorkQueue[] ws; WorkQueue q; int m, n;
+                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 && (n = q.queueSize()) > 0) {
+                            p.signalWork(q, n);
+                            if ((int)(p.ctl >> AC_SHIFT) >= 0)
+                                break;
+                        }
+                    }
+                }
+                if (p.tryCompensate()) {
+                    try {
+                        do {} while (!blocker.isReleasable() &&
+                                     !blocker.block());
+                    } finally {
                         p.incrementActiveCount();
+                    }
+                    break;
                 }
-                break;
             }
         }
+        else {
+            do {} while (!blocker.isReleasable() &&
+                         !blocker.block());
+        }
     }
 
     // AbstractExecutorService overrides.  These rely on undocumented
@@ -3142,33 +3172,52 @@
     private static final long PARKBLOCKER;
     private static final int ABASE;
     private static final int ASHIFT;
-    private static final long NEXTWORKERNUMBER;
     private static final long STEALCOUNT;
-    private static final long MAINLOCK;
+    private static final long PLOCK;
+    private static final long INDEXSEED;
+    private static final long QLOCK;
 
     static {
-        poolNumberGenerator = new AtomicInteger();
-        nextSubmitterSeed = new AtomicInteger(0x55555555);
-        modifyThreadPermission = new RuntimePermission("modifyThread");
-        defaultForkJoinWorkerThreadFactory =
-            new DefaultForkJoinWorkerThreadFactory();
-        submitters = new ThreadSubmitter();
-        int s;
+        // Establish common pool parameters
+        // TBD: limit or report ignored exceptions?
+
+        int par = 0;
+        ForkJoinWorkerThreadFactory fac = null;
+        Thread.UncaughtExceptionHandler handler = null;
+        try {
+            String pp = System.getProperty(propPrefix + "parallelism");
+            String hp = System.getProperty(propPrefix + "exceptionHandler");
+            String fp = System.getProperty(propPrefix + "threadFactory");
+            if (fp != null)
+                fac = ((ForkJoinWorkerThreadFactory)ClassLoader.
+                       getSystemClassLoader().loadClass(fp).newInstance());
+            if (hp != null)
+                handler = ((Thread.UncaughtExceptionHandler)ClassLoader.
+                           getSystemClassLoader().loadClass(hp).newInstance());
+            if (pp != null)
+                par = Integer.parseInt(pp);
+        } catch(Exception ignore) {
+        }
+
+        int s; // initialize field offsets for CAS etc
         try {
             U = sun.misc.Unsafe.getUnsafe();
             Class<?> k = ForkJoinPool.class;
-            Class<?> ak = ForkJoinTask[].class;
             CTL = U.objectFieldOffset
                 (k.getDeclaredField("ctl"));
-            NEXTWORKERNUMBER = U.objectFieldOffset
-                (k.getDeclaredField("nextWorkerNumber"));
             STEALCOUNT = U.objectFieldOffset
                 (k.getDeclaredField("stealCount"));
-            MAINLOCK = U.objectFieldOffset
-                (k.getDeclaredField("mainLock"));
+            PLOCK = U.objectFieldOffset
+                (k.getDeclaredField("plock"));
+            INDEXSEED = U.objectFieldOffset
+                (k.getDeclaredField("indexSeed"));
             Class<?> tk = Thread.class;
             PARKBLOCKER = U.objectFieldOffset
                 (tk.getDeclaredField("parkBlocker"));
+            Class<?> wk = WorkQueue.class;
+            QLOCK = U.objectFieldOffset
+                (wk.getDeclaredField("qlock"));
+            Class<?> ak = ForkJoinTask[].class;
             ABASE = U.arrayBaseOffset(ak);
             s = U.arrayIndexScale(ak);
             ASHIFT = 31 - Integer.numberOfLeadingZeros(s);
@@ -3177,31 +3226,27 @@
         }
         if ((s & (s-1)) != 0)
             throw new Error("data type scale not a power of two");
-        try { // Establish common pool
-            String pp = System.getProperty(propPrefix + "parallelism");
-            String fp = System.getProperty(propPrefix + "threadFactory");
-            String up = System.getProperty(propPrefix + "exceptionHandler");
-            ForkJoinWorkerThreadFactory fac = (fp == null) ?
-                defaultForkJoinWorkerThreadFactory :
-                ((ForkJoinWorkerThreadFactory)ClassLoader.
-                 getSystemClassLoader().loadClass(fp).newInstance());
-            Thread.UncaughtExceptionHandler ueh = (up == null)? null :
-                ((Thread.UncaughtExceptionHandler)ClassLoader.
-                 getSystemClassLoader().loadClass(up).newInstance());
-            int par;
-            if ((pp == null || (par = Integer.parseInt(pp)) <= 0))
-                par = Runtime.getRuntime().availableProcessors();
-            if (par > MAX_CAP)
-                par = MAX_CAP;
-            commonPoolParallelism = par;
-            int n = par - 1; // precompute submit mask
-            n |= n >>> 1; n |= n >>> 2; n |= n >>> 4;
-            n |= n >>> 8; n |= n >>> 16;
-            int mask = ((n + 1) << 1) - 1;
-            commonPool = new ForkJoinPool(par, mask, fac, ueh);
-        } catch (Exception e) {
-            throw new Error(e);
-        }
+
+        /*
+         * For extra caution, computations to set up pool state are
+         * here; the constructor just assigns these values to fields.
+         */
+        ForkJoinWorkerThreadFactory defaultFac =
+            defaultForkJoinWorkerThreadFactory =
+            new DefaultForkJoinWorkerThreadFactory();
+        if (fac == null)
+            fac = defaultFac;
+        if (par <= 0)
+            par = Runtime.getRuntime().availableProcessors();
+        if (par > MAX_CAP)
+            par = MAX_CAP;
+        long np = (long)(-par); // precompute initial ctl value
+        long ct = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
+
+        commonPoolParallelism = par;
+        commonPool = new ForkJoinPool(par, ct, fac, handler);
+        modifyThreadPermission = new RuntimePermission("modifyThread");
+        submitters = new ThreadLocal<Submitter>();
     }
 
 }
--- a/src/share/classes/java/util/concurrent/ForkJoinTask.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/concurrent/ForkJoinTask.java	Thu Nov 15 00:47:35 2012 -0500
@@ -285,10 +285,9 @@
      */
     private int externalAwaitDone() {
         int s;
+        ForkJoinPool.externalHelpJoin(this);
         boolean interrupted = false;
-        if ((s = status) >= 0 && ForkJoinPool.tryUnsubmitFromCommonPool(this))
-            s = doExec();
-        while (s >= 0) {
+        while ((s = status) >= 0) {
             if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
                 synchronized (this) {
                     if (status >= 0) {
@@ -302,7 +301,6 @@
                         notifyAll();
                 }
             }
-            s = status;
         }
         if (interrupted)
             Thread.currentThread().interrupt();
@@ -313,12 +311,11 @@
      * Blocks a non-worker-thread until completion or interruption.
      */
     private int externalInterruptibleAwaitDone() throws InterruptedException {
+        int s;
         if (Thread.interrupted())
             throw new InterruptedException();
-        int s;
-        if ((s = status) >= 0 && ForkJoinPool.tryUnsubmitFromCommonPool(this))
-            s = doExec();
-        while (s >= 0) {
+        ForkJoinPool.externalHelpJoin(this);
+        while ((s = status) >= 0) {
             if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
                 synchronized (this) {
                     if (status >= 0)
@@ -327,11 +324,11 @@
                         notifyAll();
                 }
             }
-            s = status;
         }
         return s;
     }
 
+
     /**
      * Implementation for join, get, quietlyJoin. Directly handles
      * only cases of already-completed, external wait, and
@@ -601,14 +598,36 @@
     }
 
     /**
+     * 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);
+        }
+    }
+
+    /**
+     * The sneaky part of sneaky throw, relying on generics
+     * limitations to evade compiler complaints about rethrowing
+     * unchecked exceptions
+     */
+    @SuppressWarnings("unchecked") static <T extends Throwable>
+        T uncheckedThrowable(final Throwable t, final Class<T> c) {
+        return (T)t; // rely on vacuous cast
+    }
+
+    /**
      * Throws exception, if any, associated with the given status.
      */
     private void reportException(int s) {
-        Throwable ex = ((s == CANCELLED) ?  new CancellationException() :
-                        (s == EXCEPTIONAL) ? getThrowableException() :
-                        null);
-        if (ex != null)
-            U.throwException(ex);
+        if (s == CANCELLED)
+            throw new CancellationException();
+        if (s == EXCEPTIONAL)
+            rethrow(getThrowableException());
     }
 
     // public methods
@@ -633,7 +652,7 @@
         if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
             ((ForkJoinWorkerThread)t).workQueue.push(this);
         else
-            ForkJoinPool.submitToCommonPool(this);
+            ForkJoinPool.commonPool.externalPush(this);
         return this;
     }
 
@@ -735,7 +754,7 @@
             }
         }
         if (ex != null)
-            U.throwException(ex);
+            rethrow(ex);
     }
 
     /**
@@ -786,7 +805,7 @@
             }
         }
         if (ex != null)
-            U.throwException(ex);
+            rethrow(ex);
         return tasks;
     }
 
@@ -969,16 +988,18 @@
                 ForkJoinWorkerThread wt = (ForkJoinWorkerThread)t;
                 p = wt.pool;
                 w = wt.workQueue;
-                s = p.helpJoinOnce(w, this); // no retries on failure
+                p.helpJoinOnce(w, this); // no retries on failure
             }
+            else
+                ForkJoinPool.externalHelpJoin(this);
             boolean canBlock = false;
             boolean interrupted = false;
             try {
                 while ((s = status) >= 0) {
-                    if (w != null && w.runState < 0)
+                    if (w != null && w.qlock < 0)
                         cancelIgnoringExceptions(this);
                     else if (!canBlock) {
-                        if (p == null || p.tryCompensate(this, null))
+                        if (p == null || p.tryCompensate())
                             canBlock = true;
                     }
                     else {
@@ -1117,9 +1138,9 @@
      */
     public boolean tryUnfork() {
         Thread t;
-        return ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
-            ((ForkJoinWorkerThread)t).workQueue.tryUnpush(this) :
-            ForkJoinPool.tryUnsubmitFromCommonPool(this);
+        return (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+                ((ForkJoinWorkerThread)t).workQueue.tryUnpush(this) :
+                ForkJoinPool.tryExternalUnpush(this));
     }
 
     /**
@@ -1131,10 +1152,12 @@
      * @return the number of tasks
      */
     public static int getQueuedTaskCount() {
-        Thread t;
-        return ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
-            ((ForkJoinWorkerThread)t).workQueue.queueSize() :
-            ForkJoinPool.getEstimatedSubmitterQueueLength();
+        Thread t; ForkJoinPool.WorkQueue q;
+        if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
+            q = ((ForkJoinWorkerThread)t).workQueue;
+        else
+            q = ForkJoinPool.commonSubmitterQueue();
+        return (q == null) ? 0 : q.queueSize();
     }
 
     /**
@@ -1151,53 +1174,7 @@
      * @return the surplus number of tasks, which may be negative
      */
     public static int getSurplusQueuedTaskCount() {
-        /*
-         * The aim of this method is to return a cheap heuristic guide
-         * for task partitioning when programmers, frameworks, tools,
-         * or languages have little or no idea about task granularity.
-         * In essence by offering this method, we ask users only about
-         * tradeoffs in overhead vs expected throughput and its
-         * variance, rather than how finely to partition tasks.
-         *
-         * In a steady state strict (tree-structured) computation,
-         * each thread makes available for stealing enough tasks for
-         * other threads to remain active. Inductively, if all threads
-         * play by the same rules, each thread should make available
-         * only a constant number of tasks.
-         *
-         * The minimum useful constant is just 1. But using a value of
-         * 1 would require immediate replenishment upon each steal to
-         * maintain enough tasks, which is infeasible.  Further,
-         * partitionings/granularities of offered tasks should
-         * minimize steal rates, which in general means that threads
-         * nearer the top of computation tree should generate more
-         * than those nearer the bottom. In perfect steady state, each
-         * thread is at approximately the same level of computation
-         * tree. However, producing extra tasks amortizes the
-         * uncertainty of progress and diffusion assumptions.
-         *
-         * So, users will want to use values larger, but not much
-         * larger than 1 to both smooth over transient shortages and
-         * hedge against uneven progress; as traded off against the
-         * cost of extra task overhead. We leave the user to pick a
-         * threshold value to compare with the results of this call to
-         * guide decisions, but recommend values such as 3.
-         *
-         * When all threads are active, it is on average OK to
-         * estimate surplus strictly locally. In steady-state, if one
-         * thread is maintaining say 2 surplus tasks, then so are
-         * others. So we can just use estimated queue length.
-         * However, this strategy alone leads to serious mis-estimates
-         * in some non-steady-state conditions (ramp-up, ramp-down,
-         * other stalls). We can detect many of these by further
-         * considering the number of "idle" threads, that are known to
-         * have zero queued tasks, so compensate by a factor of
-         * (#idle/#active) threads.
-         */
-        Thread t; ForkJoinWorkerThread wt;
-        return ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
-            (wt = (ForkJoinWorkerThread)t).workQueue.queueSize() - wt.pool.idlePerActive() :
-            0;
+        return ForkJoinPool.getSurplusQueuedTaskCount();
     }
 
     // Extension methods
@@ -1241,21 +1218,22 @@
     /**
      * Returns, but does not unschedule or execute, a task queued by
      * the current thread but not yet executed, if one is immediately
-     * available and the current thread is operating in a
-     * ForkJoinPool. There is no guarantee that this task will
-     * actually be polled or executed next. Conversely, this method
-     * may return null even if a task exists but cannot be accessed
-     * without contention with other threads.  This method is designed
+     * available. There is no guarantee that this task will actually
+     * be polled or executed next. Conversely, this method may return
+     * null even if a task exists but cannot be accessed without
+     * contention with other threads.  This method is designed
      * primarily to support extensions, and is unlikely to be useful
      * otherwise.
      *
      * @return the next task, or {@code null} if none are available
      */
     protected static ForkJoinTask<?> peekNextLocalTask() {
-        Thread t;
-        return ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
-            ((ForkJoinWorkerThread)t).workQueue.peek() :
-            null;
+        Thread t; ForkJoinPool.WorkQueue q;
+        if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
+            q = ((ForkJoinWorkerThread)t).workQueue;
+        else
+            q = ForkJoinPool.commonSubmitterQueue();
+        return (q == null) ? null : q.peek();
     }
 
     /**
@@ -1480,14 +1458,16 @@
     // Unsafe mechanics
     private static final sun.misc.Unsafe U;
     private static final long STATUS;
+
     static {
         exceptionTableLock = new ReentrantLock();
         exceptionTableRefQueue = new ReferenceQueue<Object>();
         exceptionTable = new ExceptionNode[EXCEPTION_MAP_CAPACITY];
         try {
             U = sun.misc.Unsafe.getUnsafe();
+            Class<?> k = ForkJoinTask.class;
             STATUS = U.objectFieldOffset
-                (ForkJoinTask.class.getDeclaredField("status"));
+                (k.getDeclaredField("status"));
         } catch (Exception e) {
             throw new Error(e);
         }
--- a/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java	Thu Nov 15 00:47:35 2012 -0500
@@ -31,17 +31,24 @@
     final ForkJoinPool pool;                // the pool this thread works in
 
     /**
+     * An initial name for a newly constructed worker, used until
+     * onStart can establish a useful name. This removes need to
+     * establish a name from worker startup path.
+     */
+    static final String provisionalName = "aForkJoinWorkerThread";
+
+    /**
      * Creates a ForkJoinWorkerThread operating in the given pool.
      *
      * @param pool the pool this thread works in
      * @throws NullPointerException if pool is null
      */
     protected ForkJoinWorkerThread(ForkJoinPool pool) {
-        super(pool.nextWorkerName());
-        setDaemon(true);
+        super(provisionalName); // bootstrap name
         Thread.UncaughtExceptionHandler ueh = pool.ueh;
         if (ueh != null)
             setUncaughtExceptionHandler(ueh);
+        setDaemon(true);
         this.pool = pool;
         pool.registerWorker(this.workQueue = new ForkJoinPool.WorkQueue
                             (pool, this, pool.localMode));
@@ -79,6 +86,10 @@
      * processing tasks.
      */
     protected void onStart() {
+        String pref; // replace bootstrap name
+        if (provisionalName.equals(getName()) &&
+            (pref = pool.workerNamePrefix) != null)
+            setName(pref.concat(Long.toString(getId())));
     }
 
     /**
--- a/src/share/classes/java/util/streams/ReferencePipeline.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ReferencePipeline.java	Thu Nov 15 00:47:35 2012 -0500
@@ -30,6 +30,8 @@
 import java.util.Optional;
 import java.util.functions.*;
 import java.util.streams.ops.*;
+import java.util.streams.primitives.RefToIntMapOp;
+import java.util.streams.primitives.IntStream;
 
 /**
  * A pipeline of elements that are references to objects of type <code>T</code>.
@@ -60,6 +62,11 @@
     }
 
     @Override
+    public IntStream mapToInt(IntMapper<? super U> mapper) {
+        return pipeline(new RefToIntMapOp<U>(mapper));
+    }
+
+    @Override
     public <R> Stream<R> flatMap(FlatMapper<? extends R, ? super U> mapper) {
         return pipeline(new FlatMapOp<>(mapper));
     }
@@ -95,8 +102,13 @@
     }
 
     @Override
-    public Stream<U> skip(int n) {
-        return pipeline(new SliceOp<U>(n));
+    public Stream<U> skip(int toSkip) {
+        return pipeline(new SliceOp<U>(toSkip));
+    }
+
+    @Override
+    public Stream<U> slice(int skip, int limit) {
+        return pipeline(new SliceOp<U>(skip, limit));
     }
 
     @Override
--- a/src/share/classes/java/util/streams/Sink.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/Sink.java	Thu Nov 15 00:47:35 2012 -0500
@@ -37,8 +37,6 @@
  * @author Brian Goetz
  */
 public interface Sink<T> extends Block<T> {
-    void accept(T t);
-
     /**
      * Reset the sink state to receive a fresh data set. This is used when a
      * Sink is being reused by multiple calculations.
@@ -53,9 +51,6 @@
      */
     default void end() {}
 
-    @Override
-    default void apply(T t) { accept(t); }
-
     public interface OfValue<T> extends Sink<T> {
         @Override
         default void begin(int size) {}
--- a/src/share/classes/java/util/streams/Stream.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/Stream.java	Thu Nov 15 00:47:35 2012 -0500
@@ -26,6 +26,7 @@
 
 import java.util.*;
 import java.util.functions.*;
+import java.util.streams.primitives.IntStream;
 
 /**
  * A potentially infinite sequence of elements. A stream is a consumable data
@@ -43,8 +44,12 @@
 
     <R> Stream<R> map(Mapper<? extends R, ? super T> mapper);
 
+    IntStream mapToInt(IntMapper<? super T> mapper);
+
     <R> Stream<R> flatMap(FlatMapper<? extends R, ? super T> mapper);
 
+    // @@@ flatMapToInt
+
     Stream<T> uniqueElements();
 
     Stream<T> sorted(Comparator<? super T> comparator);
@@ -65,7 +70,7 @@
      * if it contains less than or equal to {@code n} elements.
      *
      * @param n the number elements the stream should be limited to.
-     * @return the limited stream.
+     * @return the truncated stream
      */
     Stream<T> limit(int n);
 
@@ -73,11 +78,20 @@
      * Skip at most {@code n} elements.
      *
      * @param n the number of elements to be skipped.
-     * @return the skipped stream.
+     * @return the truncated stream
      */
     Stream<T> skip(int n);
 
     /**
+     * Compute a subsequence of this stream
+     *
+     * @param skip the number of elements to be skipped.
+     * @param limit the maximum length of the resulting stream
+     * @return the truncated stream
+     */
+    Stream<T> slice(int skip, int limit);
+
+    /**
      * Concatenate to the end of this stream.
      *
      * @param other the stream to concatenate.
--- a/src/share/classes/java/util/streams/StreamShapeFactory.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/StreamShapeFactory.java	Thu Nov 15 00:47:35 2012 -0500
@@ -26,6 +26,7 @@
 
 import java.util.WeakHashMap;
 import java.util.streams.ops.*;
+import java.util.streams.primitives.*;
 
 public final class StreamShapeFactory {
 
@@ -33,6 +34,8 @@
 
     public static final StreamShape<Stream> REFERENCE = lookup(Stream.class);
 
+    public static final StreamShape<IntStream> INT_VALUE = lookup(IntStream.class);
+
     private final WeakHashMap<Class<? extends BaseStream>, StreamShape> streamShapeRegistry = new WeakHashMap<>();
 
     private StreamShapeFactory() {
@@ -72,6 +75,47 @@
         };
 
         set(referenceStreamShape);
+
+        StreamShape<IntStream> intStreamShape = new StreamShape<IntStream>() {
+            @Override
+            public Class<IntStream> getStreamType() {
+                return IntStream.class;
+            }
+
+            @Override
+            public <U, V> AbstractPipeline<U, V> chain(AbstractPipeline<?, U> upstream, IntermediateOp<U, V> op) {
+                // @@@ V is constrained to an Integer, but this cannot be expressed statically unless V is pushed up
+                //     to the type
+                return new IntPipeline(upstream, op);
+            }
+
+            @Override
+            public <T> AbstractPipeline<?, T> stream(Node<T> n) throws IllegalArgumentException {
+                return new IntPipeline((IntSpliterator) n.spliterator(),
+                                       StreamOpFlags.IS_ORDERED | StreamOpFlags.IS_SIZED);
+            }
+
+            @Override
+            public <T> AbstractPipeline<?, T> parallel(Node<T> n) throws IllegalArgumentException {
+                return new IntPipeline((IntSpliterator) n.spliterator(),
+                                       StreamOpFlags.IS_ORDERED | StreamOpFlags.IS_SIZED | StreamOpFlags.IS_PARALLEL);
+            }
+
+            @Override
+            public NodeBuilder makeNodeBuilder(int sizeIfKnown) {
+                // @@@ raw types
+                return IntNodes.makeBuilder(sizeIfKnown);
+            }
+
+            @Override
+            public Node collect(ParallelPipelineHelper helper, boolean flattenTree) {
+                // @@@ raw types
+                return IntTreeUtils.collect(helper, flattenTree);
+            }
+
+        };
+
+        set(intStreamShape);
     }
 
     private void set(StreamShape<? extends BaseStream> shape) {
--- a/src/share/classes/java/util/streams/ops/AbstractTask.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/AbstractTask.java	Thu Nov 15 00:47:35 2012 -0500
@@ -107,7 +107,12 @@
     /** Is this task a leaf node?  (Only valid after <code>compute()</code> has been called on this node).
      * If the node is not a leaf node, then children will be non-null and numChildren will be positive. */
     protected boolean isLeaf() {
-        return (children == null);
+        return children == null;
+    }
+
+    /** Is this task the root node? */
+    protected boolean isRoot() {
+        return getParent() == null;
     }
 
     /**
@@ -128,14 +133,14 @@
     public void compute() {
         if (!helper.suggestSplit(spliterator)) {
             setRawResult(doLeaf());
-            helpComplete();
+            tryComplete();
         }
         else {
             int naturalSplits = spliterator.getNaturalSplits();
             if (naturalSplits == 0) {
                 // Shouldn't get here, but if we do, act like a leaf
                 setRawResult(doLeaf());
-                helpComplete();
+                tryComplete();
             }
             else if (naturalSplits == 1) {
                 // Common case -- binary splits
@@ -205,7 +210,7 @@
     public void compute() {
         if (taskCancelled()) {
             setRawResult(getEmptyResult());
-            helpComplete();
+            tryComplete();
         }
         else
             super.compute();
@@ -216,7 +221,6 @@
     }
 
     protected void cancelLaterNodes() {
-        // @@@ Avoid redundant cancels
         T parent = getParent();
         for (T sibling = this.nextSibling; sibling != null; sibling = sibling.nextSibling)
             if (!sibling.canceled)
@@ -241,6 +245,15 @@
         atomicAnswer = parent.atomicAnswer;
     }
 
+    @Override
+    public void compute() {
+        // Have we already found an answer?
+        if (atomicAnswer.get() != null)
+            tryComplete();
+        else
+            super.compute();
+    }
+
     protected void shortCircuit(R r) {
         if (r != null)
             atomicAnswer.compareAndSet(null, r);
@@ -248,28 +261,21 @@
 
     @Override
     protected void setRawResult(R r) {
-        if (getParent() == null && r != null)
-            shortCircuit(r);
+        if (isRoot()) {
+            if (r != null)
+                atomicAnswer.compareAndSet(null, r);
+        }
         else
             super.setRawResult(r);
     }
 
     @Override
     public R getRawResult() {
-        if (getParent() == null) {
+        if (isRoot()) {
             R answer = atomicAnswer.get();
             return (answer == null) ? getEmptyResult() : answer;
         }
         else
             return super.getRawResult();
     }
-
-    @Override
-    public void compute() {
-        // Have we already found an answer?
-        if (atomicAnswer.get() != null)
-            helpComplete();
-        else
-            super.compute();
-    }
 }
--- a/src/share/classes/java/util/streams/ops/ConcatOp.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/ConcatOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -61,8 +61,8 @@
 
         return new Sink.ChainedValue<T>(sink) {
             @Override
-            public void accept(T t) {
-                downstream.accept(t);
+            public void apply(T t) {
+                downstream.apply(t);
             }
 
             @Override
@@ -73,7 +73,7 @@
                 // result in buffering of the stream contents
                 Iterator<? extends T> i = stream.iterator();
                 while (i.hasNext()) {
-                    downstream.accept(i.next());
+                    downstream.apply(i.next());
                 }
                 downstream.end();
             }
--- a/src/share/classes/java/util/streams/ops/CumulateOp.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/CumulateOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -62,7 +62,7 @@
             }
 
             @Override
-            public void accept(T t) {
+            public void apply(T t) {
                 if (first) {
                     first = false;
                     state = t;
@@ -70,7 +70,7 @@
                 else {
                     state = op.operate(state, t);
                 }
-                sink.accept(state);
+                sink.apply(state);
             }
 
             @Override
--- a/src/share/classes/java/util/streams/ops/FilterOp.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/FilterOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -58,9 +58,9 @@
         Objects.requireNonNull(sink);
         return new Sink.ChainedValue<T>(sink) {
             @Override
-            public void accept(T t) {
+            public void apply(T t) {
                 if (predicate.test(t))
-                    downstream.accept(t);
+                    downstream.apply(t);
             }
         };
     }
--- a/src/share/classes/java/util/streams/ops/FlatMapOp.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/FlatMapOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -61,7 +61,7 @@
     public Sink<T> wrapSink(int flags, final Sink sink) {
         Objects.requireNonNull(sink);
         return new Sink.ChainedValue<T>(sink) {
-            public void accept(T t) {
+            public void apply(T t) {
                 mapper.flatMapInto(downstream, t);
             }
         };
--- a/src/share/classes/java/util/streams/ops/FoldOp.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/FoldOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -83,7 +83,7 @@
         }
 
         @Override
-        public void accept(T t) {
+        public void apply(T t) {
             state = reducer.combine(state, t);
         }
 
--- a/src/share/classes/java/util/streams/ops/ForEachOp.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/ForEachOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -48,7 +48,7 @@
         Objects.requireNonNull(block);
         return new ForEachOp<>(new TerminalSink<T, Void>() {
          @Override
-            public void accept(T t) {
+            public void apply(T t) {
                 block.apply(t);
             }
 
--- a/src/share/classes/java/util/streams/ops/GroupByOp.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/GroupByOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -76,7 +76,7 @@
             // Cache the sink chain, so it can be reused by all F/J leaf tasks
             Sink<S> sinkChain = helper.wrapSink(new Sink.OfValue<T>() {
                 @Override
-                public void accept(T t) {
+                public void apply(T t) {
                     final Collection<T> sb = map.computeIfAbsent(mapper.map(t), (k) -> valueFactory.make());
                     synchronized (sb) {
                         sb.add(t);
@@ -111,7 +111,7 @@
         }
 
         @Override
-        public void accept(T t) {
+        public void apply(T t) {
             K key = Objects.requireNonNull(mapper.map(t), String.format("The element %s cannot be mapped to a null key", t));
             Collection<T> c = map.get(key);
             if (c == null) {
--- a/src/share/classes/java/util/streams/ops/MapOp.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/MapOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -57,8 +57,8 @@
     public Sink<T> wrapSink(int flags, Sink sink) {
         return new Sink.ChainedValue<T>(sink) {
             @Override
-            public void accept(T t) {
-                downstream.accept(mapper.map(t));
+            public void apply(T t) {
+                downstream.apply(mapper.map(t));
             }
         };
     }
--- a/src/share/classes/java/util/streams/ops/Nodes.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/Nodes.java	Thu Nov 15 00:47:35 2012 -0500
@@ -143,6 +143,16 @@
         //
 
         @Override
+        public int hashCode() {
+            return Nodes.hashCode(this);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return (obj instanceof Iterable) && Nodes.equals(this, (Iterable<?>) obj);
+        }
+
+        @Override
         public String toString() {
             return String.format("ArrayNode[%d][%s]", array.length - curSize, Arrays.toString(array));
         }
@@ -213,6 +223,16 @@
         //
 
         @Override
+        public int hashCode() {
+            return Nodes.hashCode(this);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return (obj instanceof Iterable) && Nodes.equals(this, (Iterable<?>) obj);
+        }
+
+        @Override
         public String toString() {
             return String.format("CollectionNode[%d][%s]", c.size(), c);
         }
@@ -239,7 +259,7 @@
             nb.begin(-1);
             Iterator<? extends T> i = stream.iterator();
             while (i.hasNext())
-                nb.accept(i.next());
+                nb.apply(i.next());
             nb.end();
 
             return nb.build();
@@ -346,6 +366,16 @@
         //
 
         @Override
+        public int hashCode() {
+            return Nodes.hashCode(this);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return (obj instanceof Iterable) && Nodes.equals(this, (Iterable<?>) obj);
+        }
+
+        @Override
         public String toString() {
             if (size() < 32) {
                 return String.format("ConcNode[%s]", Arrays.stream(nodes).map(n -> n.toString()).into(new StringJoiner(",")).toString());
@@ -498,7 +528,7 @@
         }
 
         @Override
-        public void accept(T t) {
+        public void apply(T t) {
             if (curSize < array.length) {
                 array[curSize++] = t;
             } else {
@@ -514,16 +544,6 @@
         }
 
         @Override
-        public int hashCode() {
-            return Nodes.hashCode(this);
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            return (obj instanceof NodeBuilder) && Nodes.equals(this, (NodeBuilder) obj);
-        }
-
-        @Override
         public String toString() {
             return String.format("FixedNodeBuilder[%d][%s]", array.length - curSize, Arrays.toString(array));
         }
@@ -931,7 +951,7 @@
         }
 
         @Override
-        public void accept(T t) {
+        public void apply(T t) {
             assert building : "not building";
             add(t);
         }
--- a/src/share/classes/java/util/streams/ops/ReduceByOp.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/ReduceByOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -70,7 +70,7 @@
             }
 
             @Override
-            public void accept(T t) {
+            public void apply(T t) {
                 U mapped = classifier.map(t);
                 W rVal = map.get(mapped);
                 if (rVal == null)
--- a/src/share/classes/java/util/streams/ops/SeedlessFoldOp.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/SeedlessFoldOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -77,7 +77,7 @@
         }
 
         @Override
-        public void accept(T t) {
+        public void apply(T t) {
             if (empty) {
                 empty = false;
                 state = t;
@@ -89,7 +89,7 @@
         @Override
         public void combine(FoldingSink other) {
             if (!other.empty)
-                accept(other.state);
+                apply(other.state);
         }
     }
 }
--- a/src/share/classes/java/util/streams/ops/SliceOp.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/SliceOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -80,9 +80,9 @@
                 int n = skip;
 
                 @Override
-                public void accept(T t) {
+                public void apply(T t) {
                     if (n == 0) {
-                        downstream.accept(t);
+                        downstream.apply(t);
                     }
                     else {
                         n--;
@@ -92,7 +92,7 @@
         }
         else
             // No push for short-circuit operations
-            throw new IllegalStateException();
+            throw new IllegalStateException("Cannot do push-evaluation of short-circuit stream operation");
     }
 
     @Override
@@ -104,38 +104,30 @@
 
     @Override
     public <S> Node<T> evaluateParallel(ParallelPipelineHelper<S, T> helper) {
-        // Dumb serial implementation defering to iterator
-        final Iterator<T> i = wrapIterator(helper.getStreamFlags(), helper.iterator());
-        final int size = getFinalSize(helper);
-        final NodeBuilder<T> nb = Nodes.makeBuilder(size);
+        // Parallel strategy -- two cases
+        // IF we have full size information
+        // - decompose, keeping track of each leaf's (offset, size)
+        // - calculate leaf only if intersection between (offset, size) and desired slice
+        // - Construct a Node containing the appropriate sections of the appropriate leaves
+        // IF we don't
+        // - decompose, and calculate size of each leaf
+        // - on complete, if we are a left-spine node, and we have the desired slice covered, cancel rest
+        // - @@@ this can be significantly improved
 
-        nb.begin(size);
-        while (i.hasNext())
-            nb.accept(i.next());
-        nb.end();
+        // @@@ Currently we don't do the sized version at all
 
-        return nb.build();
-    }
-
-//    @Override
-//    public <S> Node<T> evaluateParallel(ParallelPipelineHelper<S, T> helper) {
-//        // Parallel strategy -- two cases
-//        // IF we have full size information
-//        // - decompose, keeping track of each leaf's (offset, size)
-//        // - calculate leaf only if intersection between (offset, size) and desired slice
-//        // - Construct a Node containing the appropriate sections of the appropriate leaves
-//        // IF we don't
-//        // - decompose, and calculate size of each leaf
-//        // - on complete, if we are a left-spine node, and we have the desired slice covered, cancel rest
-//        // - @@@ this can be significantly improved
-//
 //        Spliterator<S> spliterator = helper.spliterator();
 //        int size = spliterator.getSizeIfKnown();
 //        if (size >= 0 && helper.getOutputSizeIfKnown() == size && spliterator.isPredictableSplits())
 //            return helper.invoke(new SizedSliceTask<>(helper, skip, getFinalSize(helper)));
 //        else
-//            return helper.invoke(new SliceTask<>(helper));
-//    }
+            return helper.invoke(new SliceTask<>(helper, skip, limit));
+    }
+
+    @Override
+    public String toString() {
+        return String.format("SliceOp[skip=%d,limit=%d", skip, limit);
+    }
 
     private static class SliceIterator<T> implements Iterator<T> {
 
@@ -178,7 +170,114 @@
         }
     }
 
-    private static class SizedSliceTask<S, T> extends AbstractCancelableTask<S, T, Node<T>, SizedSliceTask<S, T>> {
+    private static class SliceTask<S, T> extends AbstractShortCircuitTask<S, T, Node<T>, SliceTask<S, T>> {
+        private final int targetOffset, targetSize;
+        private int thisNodeSize;
+
+        private SliceTask(ParallelPipelineHelper<S, T> helper, int offset, int size) {
+            super(helper);
+            targetOffset = offset;
+            targetSize = size;
+        }
+
+        private SliceTask(SliceTask<S, T> parent, Spliterator<S> spliterator) {
+            super(parent, spliterator);
+            targetOffset = parent.targetOffset;
+            targetSize = parent.targetSize;
+        }
+
+        @Override
+        protected SliceTask<S, T> makeChild(Spliterator<S> spliterator) {
+            return new SliceTask<>(this, spliterator);
+        }
+
+        @Override
+        protected Node<T> getEmptyResult() {
+            return Nodes.emptyNode();
+        }
+
+        @Override
+        protected Node<T> doLeaf() {
+            if (!isRoot()) {
+                NodeBuilder<T> nodeBuilder = Nodes.<T>makeBuilder(-1);
+                OpUtils.intoUnwrapped(helper, spliterator, nodeBuilder);
+                thisNodeSize = nodeBuilder.size();
+                return nodeBuilder.build();
+            }
+            else {
+                return truncateToNode(-1, helper.wrapIterator(spliterator.iterator()), targetOffset, targetSize);
+            }
+        }
+
+        @Override
+        public void onCompletion(CountedCompleter<?> caller) {
+            if (!isLeaf()) {
+                thisNodeSize = 0;
+                for (SliceTask<S, T> child = children; child != null; child = child.nextSibling)
+                    thisNodeSize += child.thisNodeSize;
+                if (isLeftSpine()
+                    && ((isRoot())
+                        || (targetSize >= 0 && thisNodeSize >= targetOffset + targetSize))) {
+                    ArrayList<Node> nodes = new ArrayList<>();
+                    visit(nodes, 0);
+                    Node<T> result;
+                    if (nodes.size() == 0)
+                        result = Nodes.emptyNode();
+                    else if (nodes.size() == 1)
+                        result = nodes.get(0);
+                    else
+                        result = Nodes.node(nodes.toArray(new Node[nodes.size()]));
+                    setRawResult(result);
+                    shortCircuit(result);
+                }
+            }
+        }
+
+        private void visit(ArrayList<Node> results, int offset) {
+            if (!isLeaf()) {
+                for (SliceTask<S, T> child = children; child != null; child = child.nextSibling) {
+                    child.visit(results, offset);
+                    offset += child.thisNodeSize;
+                }
+            }
+            else {
+                if (results.size() == 0) {
+                    if (offset+thisNodeSize >= targetOffset)
+                        results.add(truncateNode(getRawResult(),
+                                                 Math.max(0, targetOffset - offset),
+                                                 targetSize >= 0 ? Math.max(0, offset + thisNodeSize - (targetOffset + targetSize)) : 0));
+                }
+                else {
+                    if (targetSize == -1 || offset < targetOffset+targetSize) {
+                        results.add(truncateNode(getRawResult(), 0,
+                                                 targetSize >= 0 ? Math.max(0, offset+thisNodeSize - (targetOffset + targetSize)) : 0));
+                    }
+                }
+            }
+        }
+
+        private Node<T> truncateToNode(int size, Iterator<T> iterator, int skip, int targetSize) {
+            NodeBuilder<T> nodeBuilder = Nodes.<T>makeBuilder(size);
+            nodeBuilder.begin(size);
+            for (int i = 0; iterator.hasNext() && i < skip; i++)
+                iterator.next();
+            for (int i = 0; iterator.hasNext() && ((targetSize == -1) || (i < targetSize)); i++)
+                nodeBuilder.apply(iterator.next());
+            nodeBuilder.end();
+            return nodeBuilder.build();
+        }
+
+        private Node<T> truncateNode(Node<T> input, int skipLeft, int skipRight) {
+            if (skipLeft == 0 && skipRight == 0)
+                return input;
+            else {
+                int truncatedSize = thisNodeSize - skipLeft - skipRight;
+                return truncateToNode(truncatedSize, input.iterator(), skipLeft, truncatedSize);
+            }
+        }
+    }
+
+    private static class SizedSliceTask<S, T> extends AbstractShortCircuitTask<S, T, Node<T>, SizedSliceTask<S, T>> {
         private final int targetOffset, targetSize;
         private final int offset, size;
 
@@ -222,57 +321,44 @@
 
         @Override
         protected Node<T> doLeaf() {
-            // @@@ If we're the first or last node, peel off the irrelevant elements manually
-            return helper.into(Nodes.<T>makeBuilder(spliterator.getSizeIfKnown())).build();
+            int skipLeft = Math.max(0, targetOffset - offset);
+            int skipRight = Math.max(0, offset + size - (targetOffset + targetSize));
+            if (skipLeft == 0 && skipRight == 0)
+                return helper.into(Nodes.<T>makeBuilder(spliterator.getSizeIfKnown())).build();
+            else {
+                // If we're the first or last node that intersects the target range, peel off irrelevant elements
+                int truncatedSize = size - skipLeft - skipRight;
+                NodeBuilder<T> builder = Nodes.<T>makeBuilder(truncatedSize);
+                Sink<S> wrappedSink = helper.wrapSink(builder);
+                wrappedSink.begin(truncatedSize);
+                Iterator<S> iterator = spliterator.iterator();
+                for (int i=0; i<skipLeft; i++)
+                    iterator.next();
+                for (int i=0; i<truncatedSize; i++)
+                    wrappedSink.apply(iterator.next());
+                wrappedSink.end();
+                return builder.build();
+            }
         }
 
         @Override
         public void onCompletion(CountedCompleter<?> caller) {
             if (!isLeaf()) {
-                Node<T> result = children.getRawResult();
-                for (SizedSliceTask<S, T> child = children.nextSibling; child != null; child = child.nextSibling)
-                    result = Nodes.node(result, child.getRawResult());
+                Node<T> result = null;
+                for (SizedSliceTask<S, T> child = children.nextSibling; child != null; child = child.nextSibling) {
+                    Node<T> childResult = child.getRawResult();
+                    if (childResult == null)
+                        continue;
+                    else if (result == null)
+                        result = childResult;
+                    else
+                        result = Nodes.node(result, childResult);
+                }
                 setRawResult(result);
-                // @@@ If we're done, complete the parent and cancel later siblings
+                if (offset <= targetOffset && offset+size >= targetOffset+targetSize)
+                    shortCircuit(result);
             }
         }
     }
 
-    private static class SliceTask<S, T> extends AbstractCancelableTask<S, T, Node<T>, SliceTask<S, T>> {
-        private final int targetOffset, targetSize;
-
-        private SliceTask(ParallelPipelineHelper<S, T> helper, int offset, int size) {
-            super(helper);
-            targetOffset = offset;
-            targetSize = size;
-        }
-
-        private SliceTask(SliceTask<S, T> parent, Spliterator<S> spliterator) {
-            super(parent, spliterator);
-            targetOffset = parent.targetOffset;
-            targetSize = parent.targetSize;
-        }
-
-        @Override
-        protected SliceTask<S, T> makeChild(Spliterator<S> spliterator) {
-            return new SliceTask<>(this, spliterator);
-        }
-
-        @Override
-        protected Node<T> getEmptyResult() {
-            return Nodes.emptyNode();
-        }
-
-        @Override
-        protected Node<T> doLeaf() {
-            return helper.into(Nodes.<T>makeBuilder(spliterator.getSizeIfKnown())).build();
-        }
-
-        @Override
-        public void onCompletion(CountedCompleter<?> caller) {
-            if (!isLeaf()) {
-                // @@@
-            }
-        }
-    }
 }
--- a/src/share/classes/java/util/streams/ops/SortedOp.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/SortedOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -87,12 +87,12 @@
                 public void end() {
                     downstream.begin(pq.size());
                     while (!pq.isEmpty())
-                        downstream.accept(pq.remove());
+                        downstream.apply(pq.remove());
                     downstream.end();
                 }
 
                 @Override
-                public void accept(T t) {
+                public void apply(T t) {
                     pq.add(t);
                 }
             };
--- a/src/share/classes/java/util/streams/ops/TeeOp.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/TeeOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -48,9 +48,9 @@
     public Sink<T> wrapSink(int flags, Sink sink) {
         return new Sink.ChainedValue<T>(sink) {
             @Override
-            public void accept(T t) {
+            public void apply(T t) {
                 tee.apply(t);
-                downstream.accept(t);
+                downstream.apply(t);
             }
         };
     }
--- a/src/share/classes/java/util/streams/ops/TreeUtils.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/TreeUtils.java	Thu Nov 15 00:47:35 2012 -0500
@@ -160,7 +160,7 @@
         public void compute() {
             if (!helper.suggestSplit(spliterator)) {
                 OpUtils.intoUnwrapped(helper, spliterator, Arrays.sink(array, offset, length));
-                helpComplete();
+                tryComplete();
             }
             else {
                 int naturalSplits = spliterator.getNaturalSplits();
@@ -218,7 +218,7 @@
                 firstTask.compute();
             } else {
                 node.copyInto(array, offset);
-                helpComplete();
+                tryComplete();
             }
         }
     }
--- a/src/share/classes/java/util/streams/ops/UniqOp.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/src/share/classes/java/util/streams/ops/UniqOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -79,9 +79,9 @@
                 }
 
                 @Override
-                public void accept(T t) {
+                public void apply(T t) {
                     if (lastSeen == null || !t.equals(lastSeen)) {
-                        downstream.accept(lastSeen = t);
+                        downstream.apply(lastSeen = t);
                     }
                 }
             };
@@ -103,9 +103,9 @@
                 }
 
                 @Override
-                public void accept(T t) {
+                public void apply(T t) {
                     if (seen.add(t)) {
-                        downstream.accept(t);
+                        downstream.apply(t);
                     }
                 }
             };
@@ -207,7 +207,7 @@
                 // Cache the sink chain, so it can be reused by all F/J leaf tasks
                 Sink<S> sinkChain = helper.wrapSink(new Sink.OfValue<T>() {
                     @Override
-                    public void accept(T t) {
+                    public void apply(T t) {
                         map.putIfAbsent(t, Boolean.TRUE);
                     }
                 });
@@ -241,7 +241,7 @@
         }
 
         @Override
-        public void accept(T t) {
+        public void apply(T t) {
             set.add(t);
         }
 
@@ -278,9 +278,9 @@
         }
 
         @Override
-        public void accept(T t) {
+        public void apply(T t) {
             if (lastSeen == null || !t.equals(lastSeen)) {
-                super.accept(lastSeen = t);
+                super.apply(lastSeen = t);
             }
         }
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntBlock.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.functions.Block;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public interface IntBlock extends Block<Integer> {
+
+    @Override
+    default void apply(Integer i) {
+        Logger.getLogger(getClass().getName()).log(Level.WARNING, "{0} using boxed int", getClass().getName());
+        applyInt(i.intValue());
+    }
+
+    void applyInt(int i);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntCollectorOps.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.streams.StreamOpFlags;
+import java.util.streams.StreamShape;
+import java.util.streams.StreamShapeFactory;
+import java.util.streams.ops.CollectorOps;
+
+public final class IntCollectorOps {
+
+    private IntCollectorOps() {
+    }
+
+    private static Sequential<?> SEQUENTIAL_COLLECTOR_OP = new Sequential<>();
+
+    @SuppressWarnings("unchecked")
+    public static <E_IN> Sequential<E_IN> sequentialCollector() {
+        return (Sequential<E_IN>) SEQUENTIAL_COLLECTOR_OP;
+    }
+
+    private static Parallel<?> PARALLEL_COLLECTOR_OP = new Parallel<>();
+
+    @SuppressWarnings("unchecked")
+    public static <E_IN> Parallel<E_IN> parallelCollector() {
+        return (Parallel<E_IN>) PARALLEL_COLLECTOR_OP;
+    }
+
+    private static Terminal<?> TERMINAL_COLLECTOR_OP = new Terminal<>();
+
+    @SuppressWarnings("unchecked")
+    public static <E_IN> Terminal<E_IN> terminalCollector() {
+        return (Terminal<E_IN>) TERMINAL_COLLECTOR_OP;
+    }
+
+    public static class Parallel<E_IN> extends CollectorOps.Parallel<Integer> {
+        @Override
+        public StreamShape inputShape() { return StreamShapeFactory.INT_VALUE; }
+
+        @Override
+        public StreamShape outputShape() { return StreamShapeFactory.INT_VALUE; }
+    }
+
+    /**
+     * Collect elements into a Node, if evaluated in parallel, and force sequential evaluation on upstream operations,
+     * otherwise, if evaluated sequentially, this operation is a no-op.
+     */
+    public static class Sequential<E_IN> extends Parallel<E_IN> {
+
+        private Sequential() { }
+
+        @Override
+        public int getOpFlags() {
+            return StreamOpFlags.NOT_PARALLEL;
+        }
+    }
+
+    /**
+     * Collect elements into a Node that is the result of terminal evaluation.
+     */
+    public static class Terminal<E_IN> extends CollectorOps.Terminal<Integer> {
+        @Override
+        public StreamShape inputShape() { return StreamShapeFactory.INT_VALUE; }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntFactory.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.functions.Factory;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public interface IntFactory extends Factory<Integer> {
+
+    @Override
+    default Integer make() {
+        Logger.getLogger(getClass().getName()).log(Level.WARNING, "{0} using boxed int", getClass().getName());
+        return makeInt();
+    }
+
+    int makeInt();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntFilterOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.streams.Sink;
+import java.util.streams.StreamOpFlags;
+import java.util.streams.StreamShape;
+import java.util.streams.StreamShapeFactory;
+import java.util.streams.ops.IntermediateOp;
+
+public class IntFilterOp implements IntermediateOp<Integer, Integer> {
+    public final IntPredicate predicate;
+
+    public IntFilterOp(IntPredicate predicate) {
+        this.predicate = Objects.requireNonNull(predicate);
+    }
+
+    @Override
+    public StreamShape inputShape() { return StreamShapeFactory.INT_VALUE; }
+
+    @Override
+    public StreamShape outputShape() { return StreamShapeFactory.INT_VALUE; }
+
+    @Override
+    public int getOpFlags() {
+        return StreamOpFlags.NOT_SIZED;
+    }
+
+    @Override
+    public IntIterator wrapIterator(int flags, Iterator<Integer> source) {
+        return iterator(source, predicate);
+    }
+
+    @Override
+    public IntSink wrapSink(int flags, Sink sink) {
+        Objects.requireNonNull(sink);
+
+        IntSink intSink = Primitives.adapt(sink);
+        return new IntSink.ChainedValue(intSink) {
+            @Override
+            public void applyInt(int t) {
+                if (predicate.testInt(t))
+                    downstream.applyInt(t);
+            }
+        };
+    }
+
+    public static IntIterator iterator(Iterator<Integer> source, final IntPredicate predicate) {
+        Objects.requireNonNull(source);
+        Objects.requireNonNull(predicate);
+
+        final IntIterator intSource = Primitives.adapt(source);
+        return new IntIterator() {
+            boolean nextReady = false;
+            int nextValue;
+
+            @Override
+            public boolean hasNext() {
+                while (!nextReady && intSource.hasNext()) {
+                    nextValue = intSource.nextInt();
+                    nextReady = predicate.testInt(nextValue);
+                }
+
+                return nextReady;
+            }
+
+            @Override
+            public int nextInt() {
+                if (nextReady || hasNext()) {
+                    nextReady = false;
+                    return nextValue;
+                }
+
+                throw new NoSuchElementException();
+            }
+        };
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntForEachOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.Objects;
+import java.util.streams.StreamShape;
+import java.util.streams.StreamShapeFactory;
+import java.util.streams.ops.ForEachOp;
+
+public class IntForEachOp extends ForEachOp<Integer> {
+
+    protected IntForEachOp(IntTerminalSink<Void> sink, StreamShape shape) {
+        super(sink, shape);
+    }
+
+    public static IntForEachOp make(final IntBlock block) {
+        Objects.requireNonNull(block);
+        return new IntForEachOp(new IntTerminalSink<Void>() {
+
+            @Override
+            public void applyInt(int i) {
+                block.applyInt(i);
+            }
+
+            @Override
+            public Void getAndClearState() {
+                return null;
+            }
+        }, StreamShapeFactory.INT_VALUE);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntIterable.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.Iterator;
+import java.util.functions.Block;
+
+public interface IntIterable extends Iterable<Integer> {
+
+    @Override
+    default void forEach(Block<? super Integer> sink) {
+        if (sink instanceof IntBlock) {
+            forEach((IntBlock) sink);
+        }
+        else {
+            IntIterator remaining = iterator();
+            while (remaining.hasNext()) {
+                sink.apply(remaining.nextInt());
+            }
+        }
+    }
+
+    @Override
+    IntIterator iterator();
+
+    //
+
+    default void forEach(IntBlock sink) {
+        IntIterator remaining = iterator();
+        while (remaining.hasNext()) {
+            sink.applyInt(remaining.nextInt());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntIterator.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.Iterator;
+import java.util.functions.Block;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public interface IntIterator extends Iterator<Integer> {
+
+    // Iterator<Integer>
+
+    @Override
+    default Integer next() {
+        Logger.getLogger(getClass().getName()).log(Level.WARNING, "{0} using boxed int", getClass().getName());
+        return nextInt();
+    }
+
+
+    @Override
+    default void forEach(Block<? super Integer> sink) {
+        if (sink instanceof IntBlock) {
+            forEach((IntBlock) sink);
+        }
+        else {
+            while (hasNext()) {
+                sink.apply(nextInt());
+            }
+        }
+    }
+
+    //
+
+    int nextInt();
+
+    default void forEach(IntBlock sink) {
+        while (hasNext()) {
+            sink.applyInt(nextInt());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntLimitOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.streams.*;
+import java.util.streams.ops.Node;
+import java.util.streams.ops.NodeBuilder;
+import java.util.streams.ops.Nodes;
+import java.util.streams.ops.StatefulOp;
+
+public class IntLimitOp implements StatefulOp<Integer, Integer> {
+
+    private final int limit;
+
+    public IntLimitOp(int limit) {
+        if (limit < 0)
+            throw new IllegalArgumentException("Limit must be non-negative: " + limit);
+
+        this.limit = limit;
+    }
+
+    @Override
+    public StreamShape inputShape() { return StreamShapeFactory.INT_VALUE; }
+
+    @Override
+    public StreamShape outputShape() { return StreamShapeFactory.INT_VALUE; }
+
+
+    @Override
+    public int getOpFlags() {
+        return StreamOpFlags.NOT_SIZED | StreamOpFlags.IS_SHORT_CIRCUIT;
+    }
+
+    @Override
+    public IntSink wrapSink(int flags, Sink sink) {
+        // @@@ Cannot short circuit the sink
+        // @@@ This smells somewhat
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public IntIterator wrapIterator(int flags, Iterator<Integer> source) {
+        return iterator(Primitives.adapt(source), limit);
+    }
+
+    @Override
+    public <S> Node<Integer> evaluateParallel(ParallelPipelineHelper<S, Integer> helper) {
+        // Dumb serial implementation defering to iterator
+        final IntIterator i = wrapIterator(helper.getStreamFlags(), helper.iterator());
+
+        final IntNodeBuilder nb = IntNodes.makeBuilder(Math.min(helper.getOutputSizeIfKnown(), limit));
+        i.forEach(nb);
+        return nb.build();
+    }
+
+    public static IntIterator iterator(IntIterator source, int limit) {
+        return (limit > 0)
+               ? new LimitingIntIterator(source, limit)
+               : Primitives.emptyIntIterator();
+    }
+
+    static class LimitingIntIterator implements IntIterator {
+
+        private final IntIterator source;
+
+        private int limit;
+
+        public LimitingIntIterator(IntIterator source, int limit) {
+            Objects.requireNonNull(source);
+            if (limit < 0)
+                throw new IllegalArgumentException("Limit must be non-negative: " + limit);
+
+            this.source = source;
+            this.limit = limit;
+        }
+
+        @Override
+        public boolean hasNext() {
+            if (limit > 0)
+                return source.hasNext();
+
+            return false;
+        }
+
+        @Override
+        public int nextInt() {
+            if (!hasNext())
+                throw new NoSuchElementException("No Current Element");
+
+            limit--;
+            return source.nextInt();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntMapOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.streams.Sink;
+import java.util.streams.StreamOpFlags;
+import java.util.streams.StreamShape;
+import java.util.streams.StreamShapeFactory;
+import java.util.streams.ops.IntermediateOp;
+
+public class IntMapOp implements IntermediateOp<Integer, Integer> {
+    private final IntUnaryOperator mapper;
+
+    public IntMapOp(IntUnaryOperator mapper) {
+        this.mapper = Objects.requireNonNull(mapper);
+    }
+
+    @Override
+    public StreamShape inputShape() { return StreamShapeFactory.INT_VALUE; }
+
+    @Override
+    public StreamShape outputShape() { return StreamShapeFactory.INT_VALUE; }
+
+    @Override
+    public int getOpFlags() {
+        return StreamOpFlags.NOT_SORTED | StreamOpFlags.NOT_DISTINCT;
+    }
+
+    @Override
+    public IntIterator wrapIterator(int flags, final Iterator<Integer> source) {
+        return iterator(source, mapper);
+    }
+
+    @Override
+    public IntSink wrapSink(int flags, Sink<Integer> sink) {
+        IntSink intSink = Primitives.adapt(Objects.requireNonNull(sink));
+
+        return new IntSink.ChainedValue(intSink) {
+            @Override
+            public void applyInt(int t) {
+                downstream.applyInt(mapper.operateInt(t));
+            }
+        };
+    }
+
+    public static IntIterator iterator(final Iterator<Integer> source, final IntUnaryOperator mapper) {
+        Objects.requireNonNull(mapper);
+        IntIterator intSource = Primitives.adapt(Objects.requireNonNull(source));
+
+        return new IntIterator() {
+            @Override
+            public boolean hasNext() {
+                return intSource.hasNext();
+            }
+
+            @Override
+            public int nextInt() {
+                return mapper.operateInt(intSource.nextInt());
+            }
+        };
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntNode.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.streams.ops.Node;
+
+public interface IntNode extends Node<Integer>, IntIterable {
+
+    // Node
+
+
+    @Override
+    default Iterator<IntNode> children() {
+        return Collections.emptyIterator();
+    }
+
+    @Override
+    IntSpliterator spliterator();
+
+    @Override
+    IntNode flatten();
+
+    @Override
+    default Integer[] asArray() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    default void copyInto(Integer[] array, int offset) throws IndexOutOfBoundsException {
+        throw new UnsupportedOperationException();
+    }
+
+    //
+
+    int[] asIntArray();
+
+    void copyInto(int[] array, int offset) throws IndexOutOfBoundsException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntNodeBuilder.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.functions.UnaryOperator;
+import java.util.streams.ops.NodeBuilder;
+
+public interface IntNodeBuilder extends NodeBuilder<Integer>, IntIterable, IntSink {
+
+    @Override
+    IntNode build();
+
+    @Override
+    default void forEachUpdate(UnaryOperator<Integer> operator) {
+        if (operator instanceof IntUnaryOperator) {
+            forEachUpdate((IntUnaryOperator) operator);
+        }
+        else {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    @Override
+    IntIterator iterator();
+
+    //
+
+    void forEachUpdate(IntUnaryOperator operator);
+
+    interface Fixed extends IntNodeBuilder {
+
+        /**
+         * Get the array that holds the node content.
+         *
+         * @return the array that holds the node content.
+         */
+        int[] getContent();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntNodes.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,899 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.*;
+import java.util.functions.Block;
+import java.util.functions.UnaryOperator;
+import java.util.streams.*;
+import java.util.streams.ops.CollectorOps;
+import java.util.streams.ops.Node;
+import java.util.streams.ops.NodeBuilder;
+import java.util.streams.ops.TreeUtils;
+
+public class IntNodes {
+    public static IntNode node(final int[] array) {
+        return new IntArrayNode(array);
+    }
+
+    private static class IntArrayNode implements IntNode {
+
+        final int[] array;
+        int curSize;
+
+        IntArrayNode(int size) {
+            this.array = new int[size];
+            this.curSize = 0;
+        }
+
+        IntArrayNode(int[] array) {
+            this.array = array;
+            this.curSize = array.length;
+        }
+
+        // Node
+
+        @Override
+        public IntNode flatten() {
+            return this;
+        }
+
+        @Override
+        public IntSpliterator spliterator() {
+            return Primitives.spliterator(array, 0, curSize);
+        }
+
+        @Override
+        public int[] asIntArray() {
+            if (array.length == curSize) {
+                return array;
+            } else {
+                return Arrays.copyOf(array, curSize);
+            }
+        }
+
+        @Override
+        public void copyInto(int[] dest, int destOffset) throws IndexOutOfBoundsException {
+            System.arraycopy(array, 0, dest, destOffset, curSize);
+        }
+
+        // Traversable
+
+        @Override
+        public void forEach(IntBlock sink) {
+            for (int i = 0; i < curSize; i++) {
+                sink.applyInt(array[i]);
+            }
+        }
+
+        // Iterable
+
+        @Override
+        public IntIterator iterator() {
+            return Primitives.iterator(array, 0, curSize);
+        }
+
+        // Sized
+
+        @Override
+        public int size() {
+            return curSize;
+        }
+
+        //
+
+        @Override
+        public boolean equals(Object obj) {
+            return (obj instanceof IntIterable) && IntNodes.equals(this, (IntIterable) obj);
+        }
+
+        @Override
+        public int hashCode() {
+            return IntNodes.hashCode(this);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("IntArrayNode[%d][%s]", array.length - curSize, Arrays.toString(array));
+        }
+    }
+
+    public static IntNode node(Streamable<IntStream> s) {
+        return node(s.parallel());
+    }
+
+    public static IntNode node(IntStream stream) {
+        if (stream instanceof AbstractPipeline) {
+            // @@@ Yuk! the cast sucks, but it avoids unnecessary wrapping
+            return (IntNode) ((AbstractPipeline<Integer, Integer>) stream).pipeline(IntCollectorOps.terminalCollector());
+        }
+        else {
+            // @@@ This path can occur if there are other implementations of Stream
+            //     e.g. if the a Stream instance is a proxy
+            final IntNodeBuilder nb = makeVariableSizeBuilder();
+
+            // @@@ stream.into(sb) fails because the NodeBuilder does not implement the full contract
+            // of Collection
+            // @@@ NodeBuilder could implement Stream.Destination
+
+            nb.begin(-1);
+            stream.iterator().forEach(nb);
+
+            return nb.build();
+        }
+    }
+
+    @SafeVarargs
+    public static IntNode node(IntNode... nodes) {
+        Objects.requireNonNull(nodes);
+        if (nodes.length < 2) {
+            // @@@ The signature could be (Node<T> n1, Node<T> n2, Node<T>... rest)
+            //     but requires more work to create the final array
+            throw new IllegalArgumentException("The number of nodes must be > 1");
+        }
+        return new IntConcNode(nodes);
+    }
+
+    private static class IntConcNode implements IntNode {
+        final IntNode[] nodes;
+        int size = 0;
+
+        private IntConcNode(IntNode[] nodes) {
+            this.nodes = nodes;
+        }
+
+        // Node
+
+        @Override
+        public IntSpliterator spliterator() {
+            return new IntConcNodeSpliterator(this);
+        }
+
+        @Override
+        public int getChildCount() {
+            return nodes.length;
+        }
+
+        @Override
+        public Iterator<IntNode> children() {
+            // @@@ This is more effiecient than Arrays.iterator(nodes)
+            //     which should be updated to not create an iteratable then an iterator
+            return new Iterator<IntNode>() {
+                int i = 0;
+
+                @Override
+                public boolean hasNext() {
+                    return i < nodes.length;
+                }
+
+                @Override
+                public IntNode next() {
+                    try {
+                        return nodes[i++];
+                    } catch (IndexOutOfBoundsException e) {
+                        throw new NoSuchElementException();
+                    }
+                }
+            };
+        }
+
+        @Override
+        public IntNode flatten() {
+            return IntTreeUtils.flatten(this);
+        }
+
+        @Override
+        public int[] asIntArray() {
+            return flatten().asIntArray();
+        }
+
+        @Override
+        public void copyInto(int[] array, int offset) throws IndexOutOfBoundsException {
+            IntTreeUtils.copyTo(this, array, offset);
+        }
+
+        // Traversable
+
+        @Override
+        public void forEach(IntBlock sink) {
+            for (IntNode n : nodes)
+                n.forEach(sink);
+        }
+
+        // Iterable
+
+        @Override
+        public IntIterator iterator() {
+            // @@@ Should do a depth first search and accummulate the leaf nodes then concat the iterators
+            return Primitives.concat(Arrays.stream(nodes).map(n -> n.iterator()).iterator());
+        }
+
+        // Sized
+
+        @Override
+        public int size() {
+            if (size == 0) {
+                for (IntNode n : nodes)
+                    size += n.size();
+            }
+            return size;
+        }
+
+        //
+
+        @Override
+        public boolean equals(Object obj) {
+            return (obj instanceof IntIterable) && IntNodes.equals(this, (IntIterable) obj);
+        }
+
+        @Override
+        public int hashCode() {
+            return IntNodes.hashCode(this);
+        }
+
+        @Override
+        public String toString() {
+            if (size() < 32) {
+                return String.format("IntConcNode[%s]", Arrays.stream(nodes).map(n -> n.toString()).into(new StringJoiner(",")).toString());
+            } else {
+                return String.format("IntConcNode[size=%d]", size());
+            }
+        }
+
+        private static class IntConcNodeSpliterator implements IntSpliterator {
+            private IntNode cur;
+            private Iterator<IntNode> children;
+            private int splitsLeft;
+            private IntIterator iterator;
+
+            private IntConcNodeSpliterator(IntConcNode cur) {
+                this.cur = cur;
+                this.children = cur.children();
+                this.splitsLeft = cur.getChildCount() - 1;
+            }
+
+            public IntIterator iterator() {
+                if (iterator == null)
+                    iterator = cur.iterator();
+                return iterator;
+            }
+
+            @Override
+            public int getNaturalSplits() {
+                return splitsLeft;
+            }
+
+            @Override
+            public IntSpliterator split() {
+                if (iterator != null)
+                    throw new IllegalStateException("split after iterate");
+                else if (splitsLeft == 0)
+                    return Primitives.emptyIntSpliterator();
+                else {
+                    IntSpliterator ret = nextChild().spliterator();
+                    if (--splitsLeft == 0) {
+                        cur = nextChild();
+                        if (cur.getChildCount() > 0) {
+                            children = cur.children();
+                            splitsLeft = cur.getChildCount() - 1;
+                        }
+                        else {
+                            children = Collections.emptyIterator();
+                            splitsLeft = 0;
+                        }
+                    }
+                    return ret;
+                }
+            }
+
+            private IntNode nextChild() {
+                return children.next();
+            }
+
+            @Override
+            public void forEach(Block<? super Integer> block) {
+                if (iterator == null) {
+                    cur.forEach(block);
+                    iterator = Primitives.emptyIntIterator();
+                }
+                else {
+                    while (iterator.hasNext())
+                        block.apply(iterator.nextInt());
+                }
+            }
+
+            @Override
+            public void forEach(IntBlock block) {
+                if (iterator == null) {
+                    cur.forEach(block);
+                    iterator = Primitives.emptyIntIterator();
+                }
+                else {
+                    while (iterator.hasNext())
+                        block.applyInt(iterator.nextInt());
+                }
+            }
+
+            @Override
+            public int getSizeIfKnown() {
+                return (iterator == null) ? cur.size() : -1;
+            }
+
+            @Override
+            public boolean isPredictableSplits() {
+                return true;
+            }
+        }
+    }
+
+    // Node builders
+
+    /**
+     * Make a fixed size node builder of known size, unless the size is negative, in which case make a variable size
+     * node builder.
+     *
+     * @param size the known size, or -1 if the size is not known.
+     * @return the node builder.
+     */
+    public static IntNodeBuilder makeBuilder(int size) {
+        return (size >= 0) ? makeFixedSizeBuilder(size)
+                           : makeVariableSizeBuilder();
+    }
+
+    /**
+     * Make a fixed size builder.
+     *
+     * @param size the fixed size of the builder.
+     * @return the node builder.
+     */
+    public static IntNodeBuilder makeFixedSizeBuilder(int size) {
+        return new FixedIntNodeBuilder(size);
+    }
+
+    /**
+     * Make a variable size node builder.
+     *
+     * @return the node builder.
+     */
+    public static IntNodeBuilder makeVariableSizeBuilder() {
+        return new IntSpinedNodeBuilder();
+    }
+
+    private static class FixedIntNodeBuilder extends IntArrayNode implements IntNodeBuilder.Fixed {
+
+        private FixedIntNodeBuilder(int size) {
+            super(size);
+        }
+
+        @Override
+        public int[] getContent() {
+            return array;
+        }
+
+        //
+
+        @Override
+        public IntNode build() {
+            if (curSize < array.length) {
+                throw new IllegalStateException(String.format("Current size %d is less than fixed size %d", curSize, array.length));
+            }
+
+            return this;
+        }
+
+        @Override
+        public void forEachUpdate(IntUnaryOperator f) {
+            for (int i = 0; i < curSize; i++) {
+                array[i] = f.operateInt(array[i]);
+            }
+        }
+
+        //
+
+        @Override
+        public void begin(int size) {
+            if (size != array.length) {
+                throw new IllegalStateException(String.format("Begin size %d is not equal to fixed size %d", size, array.length));
+            }
+
+            curSize = 0;
+        }
+
+        @Override
+        public void applyInt(int i) {
+            if (curSize < array.length) {
+                array[curSize++] = i;
+            } else {
+                throw new IllegalStateException(String.format("Accept exceeded fixed size of %d", array.length));
+            }
+        }
+
+        @Override
+        public void end() {
+            if (curSize < array.length) {
+                throw new IllegalStateException(String.format("End size %d is less than fixed size %d", curSize, array.length));
+            }
+        }
+
+        @Override
+        public String toString() {
+            return String.format("FixedNodeBuilder[%d][%s]", array.length - curSize, Arrays.toString(array));
+        }
+    }
+
+
+    static class IntSpinedList implements IntIterable, Sized, IntBlock {
+
+        protected static final int MIN_CHUNK_SIZE_POWER = 4;
+        protected static final int MIN_CHUNK_SIZE = 1 << MIN_CHUNK_SIZE_POWER;
+        protected static final int MAX_CHUNK_SIZE_POWER = 30; // can't be 2 << 31 because array max is 2 << 31 - 8
+        protected static final int MAX_CHUNK_SIZE = 1 << MAX_CHUNK_SIZE_POWER;
+        protected static final int CHUNKS_PER_SIZE_POWER = 2;
+        protected static final int CHUNKS_PER_SIZE = 1 << CHUNKS_PER_SIZE_POWER;
+        protected static final int ADJUSTED_OFFSET = 1 << (MIN_CHUNK_SIZE_POWER + CHUNKS_PER_SIZE_POWER);
+        protected static final int MIN_SPINE_SIZE = 8; // @@@ something with object allocation overhead.
+        protected static final int MAX_SPINE_SIZE = (MAX_CHUNK_SIZE_POWER - MIN_CHUNK_SIZE_POWER + 1) * CHUNKS_PER_SIZE;
+
+        protected int spineIndex = 0;
+        protected int chunkIndex = 0;
+        @SuppressWarnings("unchecked")
+        protected int[][] spine = new int[MIN_SPINE_SIZE][];
+        @SuppressWarnings("unchecked")
+        protected int[] chunk = spine[spineIndex] = new int[MIN_CHUNK_SIZE];
+
+        private IntSpinedList(int initialSize) {
+            ensureCapacity(initialSize);
+        }
+
+        private IntSpinedList() {
+            this(MIN_CHUNK_SIZE);
+        }
+
+        protected final void ensureCapacity(long size) {
+            long adjusted = (Math.max(size, MIN_CHUNK_SIZE) - 1) + ADJUSTED_OFFSET;
+            int log2 = Long.SIZE - 1 - Long.numberOfLeadingZeros(adjusted);
+            int offset = (int) (((1L << (log2 - CHUNKS_PER_SIZE_POWER)) - 1) & adjusted);
+            int subchunk = (int) (adjusted - offset) >>> (log2 - CHUNKS_PER_SIZE_POWER) & (CHUNKS_PER_SIZE - 1);
+            int superchunk = (log2 - MIN_CHUNK_SIZE_POWER - CHUNKS_PER_SIZE_POWER) * CHUNKS_PER_SIZE;
+            int inChunk = superchunk + subchunk;
+
+            if(inChunk > spine.length) {
+                spine = Arrays.copyOf(spine, Math.min(inChunk, MAX_SPINE_SIZE));
+            }
+        }
+
+        protected static long totalSizeForSpineIndex(int spineIndex) {
+            assert spineIndex >= 0 && spineIndex < MAX_SPINE_SIZE : "index exceeds limit";
+            long base = 1L << (MIN_CHUNK_SIZE_POWER + CHUNKS_PER_SIZE_POWER + ((spineIndex + 1) / CHUNKS_PER_SIZE));
+            long partial = ((spineIndex + 1) % CHUNKS_PER_SIZE) * (long)sizeForSpineIndex(spineIndex);
+
+            return base + partial - ADJUSTED_OFFSET;
+        }
+
+        protected static int sizeForSpineIndex(int spineIndex) {
+            assert spineIndex >= 0 && spineIndex < MAX_SPINE_SIZE : "index exceeds limit";
+            return 1 << (MIN_CHUNK_SIZE_POWER + (spineIndex / CHUNKS_PER_SIZE));
+        }
+
+        protected void increaseCapacity() {
+            if (spineIndex + 1 == MAX_SPINE_SIZE) {
+                throw new IllegalStateException("Unable to expand capacity");
+            }
+
+            spineIndex += 1;
+            if(spineIndex == spine.length) {
+                spine = Arrays.copyOf(spine, Math.min(spine.length + MIN_SPINE_SIZE, MAX_SPINE_SIZE));
+            }
+
+            if (null == spine[spineIndex]) {
+                @SuppressWarnings("unchecked")
+                int[] newArray = new int[IntSpinedList.sizeForSpineIndex(spineIndex)];
+                spine[spineIndex] = newArray;
+            }
+
+            chunk = spine[spineIndex];
+            chunkIndex = 0;
+        }
+
+        @Override
+        public IntIterator iterator() {
+            return iterator(0);
+        }
+
+        private IntIterator iterator(int startChunk) {
+            return new IntIterator() {
+                int currentChunk = startChunk;
+                int currentIndex = 0;
+
+                @Override
+                public boolean hasNext() {
+                    return currentChunk < IntSpinedList.this.spineIndex ||
+                           (currentChunk == IntSpinedList.this.spineIndex &&
+                            currentIndex <  IntSpinedList.this.chunkIndex);
+                }
+
+                @Override
+                public int nextInt() {
+                    if (!hasNext()) {
+                        throw new NoSuchElementException();
+                    }
+
+                    int result = spine[currentChunk][currentIndex++];
+
+                    if (currentIndex == spine[currentChunk].length) {
+                        currentIndex = 0;
+                        currentChunk++;
+                    }
+
+                    return result;
+                }
+            };
+        }
+
+        @Override
+        public void applyInt(int i) {
+            if (chunkIndex == chunk.length) {
+                increaseCapacity();
+            }
+            chunk[chunkIndex++] = i;
+        }
+
+        public void clear() {
+            // reset position.
+            chunk = spine[spineIndex = 0];
+            chunkIndex = 0;
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return (spineIndex == 0) && (chunkIndex == 0);
+        }
+
+        @Override
+        public int size() {
+            return (spineIndex > 0)
+                   ? (int)Math.min(Integer.MAX_VALUE, totalSizeForSpineIndex(spineIndex - 1) + chunkIndex)
+                   : chunkIndex;
+        }
+
+        public IntStream stream() {
+            return Primitives.stream(this, StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED);
+        }
+
+        public IntStream parallel() {
+            return Primitives.stream(spliterator(), StreamOpFlags.IS_ORDERED);
+        }
+
+        protected IntSpliterator spliterator() {
+            return new SpinedListSpliterator();
+        }
+
+        class SpinedListSpliterator implements IntSpliterator, IntIterator {
+            IntIterator iterator = null;
+
+            boolean traversing = false;
+
+            boolean isSpinedSpliterator = true;
+
+            int spineOffset = 0;
+
+            int[] elements;
+
+            int offset;
+
+            int endOffset;
+
+            SpinedListSpliterator() {
+                if (spineOffset == spineIndex) {
+                    isSpinedSpliterator = false;
+                    elements = chunk;
+                    offset = 0;
+                    endOffset = chunkIndex;
+                }
+            }
+
+            private SpinedListSpliterator(int[] content, int offset, int endOffset) {
+                this.spineOffset = spineIndex;
+                this.elements = content;
+                this.offset = offset;
+                this.endOffset = endOffset;
+            }
+
+            @Override
+            public int getSizeIfKnown() {
+                if (isSpinedSpliterator) {
+                    // @@@ This will not return the correct known size if iterated on
+                    int result = IntSpinedList.this.size();
+                    if (spineOffset > 0) {
+                        result -= IntSpinedList.totalSizeForSpineIndex(spineOffset - 1);
+                    }
+
+                    return result;
+                } else {
+                    return endOffset - offset;
+                }
+            }
+
+            @Override
+            public IntIterator iterator() {
+                traversing = true;
+
+                if (iterator != null) {
+                    return iterator;
+                }
+
+                if (isSpinedSpliterator) {
+                    return iterator = IntSpinedList.this.iterator(spineOffset);
+                } else {
+                    return iterator = this;
+                }
+            }
+
+            @Override
+            public void forEach(Block<? super Integer> block) {
+                traversing = true;
+
+                IntIterator.super.forEach(block);
+            }
+
+            @Override
+            public void forEach(IntBlock block) {
+                traversing = true;
+
+                if (isSpinedSpliterator) {
+                    iterator().forEach(block);
+                } else {
+                    for (int i = offset; i < endOffset; i++) {
+                        block.applyInt(elements[i]);
+                    }
+                    // update only once; reduce heap write traffic
+                    offset = endOffset;
+                }
+            }
+
+            @Override
+            public int nextInt() {
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+
+                return elements[offset++];
+            }
+
+            @Override
+            public final boolean hasNext() {
+                return offset < endOffset;
+            }
+
+            @Override
+            public boolean isPredictableSplits() {
+                return true;
+            }
+
+            @Override
+            public int getNaturalSplits() {
+                if (traversing) {
+                    return 0;
+                }
+
+                if (isSpinedSpliterator) {
+                    return spineIndex - spineOffset + (chunkIndex > 1 ? 1 : 0);
+                } else {
+                    return (endOffset - offset > 1) ? 1 : 0;
+                }
+            }
+
+            @Override
+            public IntSpliterator split() {
+                if (traversing) {
+                    throw new IllegalStateException("split after starting traversal");
+                }
+
+                if (isSpinedSpliterator) {
+                    IntSpliterator ret = Primitives.spliterator(spine[spineOffset++]);
+
+                    if (spineOffset == spineIndex) {
+                        isSpinedSpliterator = false;
+                        elements = chunk;
+                        offset = 0;
+                        endOffset = chunkIndex;
+                    }
+
+                    return ret;
+                } else {
+                    int mid = (endOffset - offset) / 2;
+                    IntSpliterator ret = Primitives.spliterator(elements, offset, mid);
+                    offset += mid;
+                    return ret;
+                }
+            }
+        }
+    }
+
+    private static class IntSpinedNodeBuilder extends IntSpinedList implements IntNode, IntNodeBuilder {
+
+        private boolean building = false;
+
+        @Override
+        public IntStream stream() {
+            assert !building : "during building";
+            return super.stream();
+        }
+
+        @Override
+        public IntStream parallel() {
+            assert !building : "during building";
+            return super.parallel();
+        }
+
+        public IntIterable asIterable() {
+            assert !building : "during building";
+            return this;
+        }
+
+        protected IntIterator iterator(int startChunk) {
+            assert !building : "during building";
+            return super.iterator(startChunk);
+        }
+
+        @Override
+        public IntSpliterator spliterator() {
+            assert !building : "during building";
+            return super.spliterator();
+        }
+
+        @Override
+        public void forEach(IntBlock block) {
+            // completed chunks
+            for (int ci = 0; ci < spineIndex; ci++) {
+                int[] chunk = spine[ci];
+                for (int i = 0; i < chunk.length; i++) {
+                    block.applyInt(chunk[i]);
+                }
+            }
+
+            // current chunk
+            for (int i = 0; i < chunkIndex; i++) {
+                block.applyInt(chunk[i]);
+            }
+        }
+
+        @Override
+        public void forEachUpdate(IntUnaryOperator f) {
+            for (int eachChunk = 0; eachChunk <= spineIndex; eachChunk++) {
+                int[] aChunk = spine[eachChunk];
+                int bounds = (eachChunk != spineIndex) ? aChunk.length : chunkIndex;
+                for (int element = 0; element < bounds; element++) {
+                    aChunk[element] = f.operateInt(aChunk[element]);
+                }
+            }
+        }
+
+        //
+        @Override
+        public void begin(int size) {
+            assert !building : "was already building";
+            building = true;
+            clear();
+            ensureCapacity(size);
+        }
+
+        @Override
+        public void applyInt(int i) {
+            super.applyInt(i);
+        }
+
+        @Override
+        public void end() {
+            assert building : "was not building";
+            building = false;
+            // @@@ check begin(size) and size
+        }
+
+        @Override
+        public void copyInto(int[] array, int offset) throws IndexOutOfBoundsException {
+            assert !building : "during building";
+            int finalOffset = offset + size();
+            if (finalOffset > array.length || finalOffset < offset) {
+                throw new IndexOutOfBoundsException("does not fit");
+            }
+
+            // full chunks
+            for (int eachChunk = 0; eachChunk < spineIndex; eachChunk++) {
+                int[] aChunk = spine[eachChunk];
+                System.arraycopy(aChunk, 0, array, offset, aChunk.length);
+                offset += aChunk.length;
+            }
+
+            // final bit.
+            System.arraycopy(spine[spineIndex], 0, array, offset, chunkIndex);
+        }
+
+        @Override
+        public int[] asIntArray() {
+            assert !building : "during building";
+            int[] result = new int[size()]; // will fail for size == MAX_VALUE
+
+            copyInto(result, 0);
+
+            return result;
+        }
+
+        @Override
+        public IntNode build() {
+            assert !building : "during building";
+            return this;
+        }
+
+        @Override
+        public IntNode flatten() {
+            return this;
+        }
+
+        //
+
+        @Override
+        public boolean equals(Object obj) {
+            return (obj instanceof IntIterable) && IntNodes.equals(this, (IntIterable) obj);
+        }
+
+        @Override
+        public int hashCode() {
+            return IntNodes.hashCode(this);
+        }
+    }
+
+    private static boolean equals(IntIterable a, IntIterable b) {
+        Objects.requireNonNull(a);
+        Objects.requireNonNull(b);
+
+        if(a instanceof Sized && b instanceof Sized) {
+            if ( ((Sized) a).size() != ((Sized) b).size()) {
+                return false;
+            }
+        }
+
+        IntIterator it1 = a.iterator();
+        IntIterator it2 = b.iterator();
+        while (it1.hasNext() && it2.hasNext()) {
+            if (it1.nextInt() != it2.nextInt()) {
+                return false;
+            }
+        }
+
+        return !(it1.hasNext() || it2.hasNext());
+    }
+
+    private static <T> int hashCode(IntIterable it) {
+        int h = 0;
+
+        IntIterator i = it.iterator();
+        while(i.hasNext()) {
+            h = 31 * h + i.nextInt();
+        }
+        return h;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntPipeline.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.Optional;
+import java.util.functions.IntBinaryOperator;
+import java.util.streams.AbstractPipeline;
+import java.util.streams.Stream;
+import java.util.streams.StreamShapeFactory;
+import java.util.streams.ops.IntermediateOp;
+
+public class IntPipeline<E_IN> extends AbstractPipeline<E_IN, Integer> implements IntStream {
+
+    private IntIterator iterator;
+
+    public IntPipeline(IntSpliterator source, int flags) {
+        super(source, flags, StreamShapeFactory.INT_VALUE);
+    }
+
+    public IntPipeline(AbstractPipeline<?, E_IN> upstream, IntermediateOp<E_IN, Integer> op) {
+        super(upstream, op);
+    }
+
+    //
+
+    @Override
+    public IntIterator iterator() {
+        if (iterator == null) {
+            iterator = Primitives.adapt(super.iterator());
+        }
+        return iterator;
+    }
+
+    //
+
+    @Override
+    public Stream<Integer> boxed() {
+        return pipeline(new IntToIntegerOp());
+    }
+
+    @Override
+    public IntStream map(IntUnaryOperator mapper) {
+        return pipeline(new IntMapOp(mapper));
+    }
+
+    @Override
+    public IntStream filter(IntPredicate predicate) {
+        return pipeline(new IntFilterOp(predicate));
+    }
+
+    @Override
+    public IntStream tee(IntBlock block) {
+        return pipeline(new IntTeeOp(block));
+    }
+
+    @Override
+    public IntStream sorted() {
+        return pipeline(new IntSortedOp());
+    }
+
+    @Override
+    public void forEach(IntBlock block) {
+        pipeline(IntForEachOp.make(block));
+    }
+
+    @Override
+    public int sum() {
+        return pipeline(new IntSumOp());
+    }
+
+    @Override
+    public int[] toArray() {
+        return pipeline(IntToArrayOp.singleton());
+    }
+
+    //
+
+    @Override
+    public IntStream cumulate(IntBinaryOperator operator) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public IntStream uniqueElements() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public IntStream limit(int n) {
+        return pipeline(new IntLimitOp(n));
+    }
+
+    @Override
+    public IntStream skip(int n) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public IntStream concat(IntStream other) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public IntStream sequential() {
+        return pipeline(IntCollectorOps.sequentialCollector());
+    }
+
+    @Override
+    public IntStream unordered() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int reduce(int base, IntBinaryOperator op) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Optional<Integer> reduce(IntBinaryOperator op) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean anyMatch(IntPredicate predicate) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean allMatch(IntPredicate predicate) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean noneMatch(IntPredicate predicate) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Optional<Integer> findFirst() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Optional<Integer> findAny() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int min() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int max() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public double average() {
+        throw new UnsupportedOperationException();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntPredicate.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+public interface IntPredicate {
+
+    boolean testInt(int i);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntSink.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.Objects;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.streams.Sink;
+
+public interface IntSink extends Sink<Integer>, IntBlock {
+
+    public static abstract class ChainedValue implements IntSink {
+        protected final IntSink downstream;
+
+        public ChainedValue(IntSink downstream) {
+            this.downstream = Objects.requireNonNull(downstream);
+        }
+
+        @Override
+        public void begin(int size) {
+            downstream.begin(size);
+        }
+
+        @Override
+        public void end() {
+            downstream.end();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntSortedOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.*;
+import java.util.concurrent.ForkJoinUtils;
+import java.util.streams.*;
+import java.util.streams.ops.*;
+
+public class IntSortedOp implements StatefulOp<Integer, Integer> {
+
+    @Override
+    public StreamShape inputShape() { return StreamShapeFactory.INT_VALUE; }
+
+    @Override
+    public StreamShape outputShape() { return StreamShapeFactory.INT_VALUE; }
+
+    @Override
+    public int getOpFlags() {
+        return StreamOpFlags.IS_SORTED | StreamOpFlags.IS_ORDERED;
+    }
+
+    @Override
+    public IntSink wrapSink(int flags, Sink sink) {
+        IntSink intSink = Primitives.adapt(sink);
+        return new IntSink.ChainedValue(intSink) {
+            IntNodeBuilder nb;
+
+            @Override
+            public void begin(int size) {
+                nb = IntNodes.makeBuilder(size);
+            }
+
+            @Override
+            public void end() {
+                int[] ints = nb.build().asIntArray();
+                Arrays.sort(ints);
+                downstream.begin(ints.length);
+                for (int i = 0; i < ints.length; i++) {
+                    downstream.applyInt(ints[i]);
+                }
+                downstream.end();
+            }
+
+            @Override
+            public void applyInt(int t) {
+                nb.applyInt(t);
+            }
+        };
+    }
+
+    @Override
+    public IntIterator wrapIterator(int flags, Iterator<Integer> iterator) {
+        Objects.requireNonNull(iterator);
+        return iterator(iterator);
+    }
+
+    public static IntIterator iterator(Iterator<Integer> iterator) {
+        Objects.requireNonNull(iterator);
+
+        IntIterator intIterator = Primitives.adapt(iterator);
+        IntNodeBuilder nb = IntNodes.makeVariableSizeBuilder();
+        intIterator.forEach(nb);
+
+        int[] ints = nb.build().asIntArray();
+        Arrays.sort(ints);
+
+        return Primitives.iterator(ints);
+    }
+
+    @Override
+    public <P_IN> Node<Integer> evaluateSequential(PipelineHelper<P_IN, Integer> helper) {
+        IntNode n = (IntNode) helper.collectOutput().flatten();
+
+        int[] content = n.asIntArray();
+        Arrays.sort(content);
+
+        return IntNodes.node(content);
+    }
+
+    @Override
+    public <P_IN> Node<Integer> evaluateParallel(ParallelPipelineHelper<P_IN, Integer> helper) {
+        IntNode n = (IntNode) helper.collectOutput().flatten();
+
+        int[] content = n.asIntArray();
+        Arrays.sort(content);
+
+        return IntNodes.node(content);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntSpliterator.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.Iterator;
+import java.util.functions.Block;
+import java.util.streams.Spliterator;
+import java.util.streams.Streams;
+
+public interface IntSpliterator extends Spliterator<Integer> {
+
+    // Spliterator<Integer>
+
+    @Override
+    IntSpliterator split();
+
+    @Override
+    IntIterator iterator();
+
+    @Override
+    default void forEach(Block<? super Integer> sink) {
+        if (sink instanceof IntBlock) {
+            forEach((IntBlock) sink);
+        }
+        else {
+            IntIterator remaining = iterator();
+            while (remaining.hasNext()) {
+                sink.apply(remaining.nextInt());
+            }
+        }
+    }
+
+    //
+
+    default void forEach(IntBlock sink) {
+        IntIterator remaining = iterator();
+        while (remaining.hasNext()) {
+            sink.apply(remaining.nextInt());
+        }
+    }
+
+    /**
+     * An IntSpliterator that can only be used to sequentially traverse a source.
+     */
+    public interface Sequential extends IntSpliterator {
+        @Override
+        default int getNaturalSplits() {
+            return 0;
+        }
+
+        @Override
+        default IntSpliterator split() {
+            return Primitives.emptyIntSpliterator();
+        }
+
+        @Override
+        default boolean isPredictableSplits() {
+            return false;
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntStream.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.functions.*;
+import java.util.streams.BaseStream;
+import java.util.streams.Stream;
+import java.util.streams.StreamShape;
+import java.util.streams.StreamShapeFactory;
+
+public interface IntStream extends BaseStream<Integer> {
+
+    // BaseStream
+
+    @Override
+    IntIterator iterator();
+
+    //
+
+    // @@@ Do the following make sense?
+
+//    <R> Stream<R> map(Mapper<? extends R, ? super T> mapper);
+//
+//    <R> Stream<R> flatMap(FlatMapper<? extends R, ? super T> mapper);
+//
+//    <A extends Destination<? super T>> A into(A target);
+//
+//    <U> Map<U, Collection<T>> groupBy(Mapper<? extends U, ? super T> classifier);
+//
+//    <U, W> Map<U, W> reduceBy(Mapper<? extends U, ? super T> classifier,
+//                              Factory<W> baseFactory,
+//                              Combiner<W, W, T> reducer);
+//
+//    <U> U fold(Factory<U> baseFactory,
+//               Combiner<U, U, T> reducer,
+//               BinaryOperator<U> combiner);
+
+    //
+
+    Stream<Integer> boxed();
+
+    IntStream map(IntUnaryOperator mapper);
+
+    IntStream cumulate(IntBinaryOperator operator);
+
+    IntStream filter(IntPredicate predicate);
+
+    IntStream tee(IntBlock block);
+
+    IntStream sorted();
+
+    IntStream uniqueElements();
+
+    IntStream limit(int n);
+
+    IntStream skip(int n);
+
+    IntStream concat(IntStream other);
+
+    IntStream sequential();
+
+    IntStream unordered();
+
+    int reduce(int base, IntBinaryOperator op);
+
+    Optional<Integer> reduce(IntBinaryOperator op);
+
+    boolean anyMatch(IntPredicate predicate);
+
+    boolean allMatch(IntPredicate predicate);
+
+    boolean noneMatch(IntPredicate predicate);
+
+    Optional<Integer> findFirst();
+
+    Optional<Integer> findAny();
+
+    void forEach(IntBlock block);
+
+    int sum();
+
+    int min();
+
+    int max();
+
+    double average();
+
+    int[] toArray();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntSumOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.streams.ParallelPipelineHelper;
+import java.util.streams.PipelineHelper;
+import java.util.streams.StreamShape;
+import java.util.streams.StreamShapeFactory;
+import java.util.streams.ops.OpUtils;
+import java.util.streams.ops.TerminalOp;
+
+public class IntSumOp implements TerminalOp<Integer, Integer> {
+
+    @Override
+    public StreamShape inputShape() {
+        return StreamShapeFactory.INT_VALUE;
+    }
+
+    @Override
+    public <P_IN> Integer evaluateSequential(PipelineHelper<P_IN, Integer> helper) {
+        return helper.into(new SumIntSink()).getAndClearState();
+    }
+
+    @Override
+    public <P_IN> Integer evaluateParallel(ParallelPipelineHelper<P_IN, Integer> helper) {
+        return OpUtils.parallelReduce(helper, () -> new SumIntSink());
+    }
+
+    private class SumIntSink implements OpUtils.AccumulatingSink<Integer, Integer, SumIntSink>, IntSink {
+        int sum;
+
+        @Override
+        public void begin(int size) {
+            sum = 0;
+        }
+
+        @Override
+        public void clearState() {
+            sum = 0;
+        }
+
+        @Override
+        public Integer getAndClearState() {
+            int r = sum;
+            sum = 0;
+            return r;
+        }
+
+        @Override
+        public void applyInt(int t) {
+            sum += t;
+        }
+
+        @Override
+        public void combine(SumIntSink other) {
+            sum += other.sum;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntTeeOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.streams.Sink;
+import java.util.streams.StreamShape;
+import java.util.streams.StreamShapeFactory;
+import java.util.streams.ops.IntermediateOp;
+
+public class IntTeeOp implements IntermediateOp<Integer, Integer> {
+    public final IntBlock tee;
+
+    public IntTeeOp(IntBlock tee) {
+        this.tee = Objects.requireNonNull(tee);
+    }
+
+    @Override
+    public StreamShape inputShape() { return StreamShapeFactory.INT_VALUE; }
+
+    @Override
+    public StreamShape outputShape() { return StreamShapeFactory.INT_VALUE; }
+
+    @Override
+    public IntIterator wrapIterator(int flags, final Iterator<Integer> source) {
+        return iterator(source, tee);
+    }
+
+    @Override
+    public IntSink wrapSink(int flags, Sink sink) {
+        Objects.requireNonNull(sink);
+
+        IntSink intSink = Primitives.adapt(sink);
+        return new IntSink.ChainedValue(intSink) {
+            @Override
+            public void applyInt(int t) {
+                tee.applyInt(t);
+                downstream.applyInt(t);
+            }
+        };
+    }
+
+    public static IntIterator iterator(final Iterator<Integer> source, final IntBlock tee) {
+        Objects.requireNonNull(source);
+        Objects.requireNonNull(tee);
+
+        final IntIterator intSource = Primitives.adapt(source);
+        return new IntIterator() {
+            @Override
+            public boolean hasNext() {
+                return intSource.hasNext();
+            }
+
+            @Override
+            public int nextInt() {
+                int next = intSource.next();
+                tee.applyInt(next);
+                return next;
+            }
+        };
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntTerminalSink.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,29 @@
+package java.util.streams.primitives;/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.streams.TerminalSink;
+
+public interface IntTerminalSink<T> extends TerminalSink<Integer, T>, IntSink {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntToArrayOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.streams.ParallelPipelineHelper;
+import java.util.streams.PipelineHelper;
+import java.util.streams.StreamShape;
+import java.util.streams.StreamShapeFactory;
+import java.util.streams.ops.Nodes;
+import java.util.streams.ops.TerminalOp;
+import java.util.streams.ops.TreeUtils;
+
+public class IntToArrayOp implements TerminalOp<Integer, int[]> {
+
+    private final static IntToArrayOp INSTANCE = new IntToArrayOp();
+
+    public static IntToArrayOp singleton() {
+        return INSTANCE;
+    }
+
+    @Override
+    public StreamShape inputShape() {
+        return StreamShapeFactory.INT_VALUE;
+    }
+
+    @Override
+    public <P_IN> int[] evaluateSequential(PipelineHelper<P_IN, Integer> helper) {
+        IntNode n = (IntNode) helper.collectOutput().flatten();
+        return n.asIntArray();
+    }
+
+    @Override
+    public <P_IN> int[] evaluateParallel(ParallelPipelineHelper<P_IN, Integer> helper) {
+        IntNode n = (IntNode) helper.collectOutput().flatten();
+        return n.asIntArray();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntToIntegerOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,85 @@
+package java.util.streams.primitives;/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.streams.Sink;
+import java.util.streams.StreamShape;
+import java.util.streams.StreamShapeFactory;
+import java.util.streams.ops.IntermediateOp;
+
+public class IntToIntegerOp implements IntermediateOp<Integer, Integer> {
+
+    @Override
+    public StreamShape inputShape() { return StreamShapeFactory.INT_VALUE; }
+
+    @Override
+    public StreamShape outputShape() { return StreamShapeFactory.REFERENCE; }
+
+    @Override
+    public Iterator<Integer> wrapIterator(int flags, final Iterator<Integer> source) {
+        return iterator(source);
+    }
+
+    @Override
+    public IntSink wrapSink(int flags, final Sink sink) {
+        Objects.requireNonNull(sink);
+
+        return new IntSink() {
+            @Override
+            public void begin(int size) {
+                sink.begin(size);
+            }
+
+            @Override
+            public void applyInt(int t) {
+                sink.apply(t);
+            }
+
+            @Override
+            public void end() {
+                sink.end();
+            }
+        };
+    }
+
+    public static Iterator<Integer> iterator(final Iterator<Integer> source) {
+        Objects.requireNonNull(source);
+
+        final IntIterator intSource = Primitives.adapt(source);
+        return new Iterator<Integer>() {
+            @Override
+            public boolean hasNext() {
+                return intSource.hasNext();
+            }
+
+            @Override
+            public Integer next() {
+                int next = intSource.next();
+                return next;
+            }
+        };
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntTreeUtils.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.Iterator;
+import java.util.concurrent.CountedCompleter;
+import java.util.streams.ParallelPipelineHelper;
+import java.util.streams.PipelineHelper;
+import java.util.streams.Spliterator;
+import java.util.streams.ops.AbstractTask;
+import java.util.streams.ops.Node;
+import java.util.streams.ops.OpUtils;
+
+/**
+ * Collector
+ *
+ * @author Brian Goetz
+ */
+public class IntTreeUtils {
+
+    private IntTreeUtils() {
+        throw new Error("no instances");
+    }
+
+    // @@@ make public ?
+    private static <P_IN> IntNode collectSequentially(PipelineHelper<P_IN, Integer> helper, Spliterator<P_IN> spliterator) {
+//        NodeBuilder<Integer> builder = OpUtils.makeNodeBuilderFor(helper, spliterator);
+        // @@@ create builder
+        IntNodeBuilder builder = IntNodes.makeBuilder(helper.getOutputSizeIfKnown() >= 0 ? spliterator.getSizeIfKnown() : -1);
+        OpUtils.intoWrapped(spliterator, helper.wrapSink(builder));
+        return builder.build();
+    }
+
+    public static <P_IN> IntNode collect(ParallelPipelineHelper<P_IN, Integer> helper,
+                                         boolean flattenTree) {
+        Spliterator<P_IN> spliterator = helper.spliterator();
+        if (!helper.suggestSplit(spliterator)) {
+            return collectSequentially(helper, spliterator);
+        }
+        else {
+            int size = spliterator.getSizeIfKnown();
+            if (size >= 0 && helper.getOutputSizeIfKnown() == size && spliterator.isPredictableSplits()) {
+                // @@@ create array
+                int[] array = new int[size];
+                helper.invoke(new SizedCollectorTask<>(spliterator, helper, array));
+                // @@@ create node
+                return IntNodes.node(array);
+            }
+            else {
+                CollectorTask<P_IN> task = new CollectorTask<>(helper);
+                helper.invoke(task);
+                IntNode node = task.getRawResult();
+
+                // @@@ using default F/J pool, will that be different from that used by helper.invoke?
+                return flattenTree ? flatten(node) : node;
+            }
+        }
+    }
+
+    public static IntNode flatten(IntNode node) {
+        if (node.getChildCount() > 0) {
+            // @@@ create array
+            int[] array = new int[node.size()];
+            new ToArrayTask(node, array, 0).invoke();
+            // @@@ create node
+            return IntNodes.node(array);
+        } else {
+            return node;
+        }
+    }
+
+    public static void copyTo(IntNode node, int[] array, int offset) {
+        // @@@ Currently only used by IntNodes.ConcNode
+        if (node.getChildCount() > 0) {
+            new ToArrayTask(node, array, offset).invoke();
+        } else {
+            node.copyInto(array, offset);
+        }
+    }
+
+    private static class CollectorTask<T> extends AbstractTask<T, Integer, IntNode, CollectorTask<T>> {
+        private final ParallelPipelineHelper<T, Integer> helper;
+
+        private CollectorTask(ParallelPipelineHelper<T, Integer> helper) {
+            super(helper);
+            this.helper = helper;
+        }
+
+        private CollectorTask(CollectorTask<T> parent, Spliterator<T> spliterator) {
+            super(parent, spliterator);
+            helper = parent.helper;
+        }
+
+        @Override
+        protected CollectorTask<T> makeChild(Spliterator<T> spliterator) {
+            return new CollectorTask<>(this, spliterator);
+        }
+
+        @Override
+        protected IntNode doLeaf() {
+            return collectSequentially(helper, spliterator);
+        }
+
+        @Override
+        public void onCompletion(CountedCompleter caller) {
+            if (!isLeaf()) {
+                @SuppressWarnings("unchecked")
+                IntNode[] nodes = new IntNode[numChildren];
+                int idx = 0;
+                for (CollectorTask<T> cur = children; cur != null; cur = cur.nextSibling)
+                    nodes[idx++] = cur.getRawResult();
+
+                // @@@ conc nodes
+                setRawResult(IntNodes.node(nodes));
+            }
+        }
+    }
+
+    private static class SizedCollectorTask<T> extends CountedCompleter<Void> {
+        private final Spliterator<T> spliterator;
+        private final ParallelPipelineHelper<T, Integer> helper;
+        private final int[] array;
+        private int offset;
+        private int length;
+
+        private SizedCollectorTask(Spliterator<T> spliterator, ParallelPipelineHelper<T, Integer> helper, int[] array) {
+            this.spliterator = spliterator;
+            this.helper = helper;
+            this.array = array;
+            this.offset = 0;
+            this.length = array.length;
+        }
+
+        private SizedCollectorTask(SizedCollectorTask<T> parent, Spliterator<T> spliterator, int offset, int length) {
+            super(parent);
+            this.spliterator = spliterator;
+            this.helper = parent.helper;
+            this.array = parent.array;
+            this.offset = offset;
+            this.length = length;
+
+            if (offset < 0 || length < 0 || (offset + length - 1 >= array.length)) {
+                throw new IllegalArgumentException(
+                        String.format("offset and length interval [%d, %d + %d) is not within array size interval [0, %d)",
+                                      offset, offset, length, array.length));
+            }
+        }
+
+        @Override
+        public void compute() {
+            if (!helper.suggestSplit(spliterator)) {
+                // @@@ create sink
+                OpUtils.intoUnwrapped(helper, spliterator, Primitives.sink(array, offset, length));
+                tryComplete();
+            }
+            else {
+                int naturalSplits = spliterator.getNaturalSplits();
+                setPendingCount(naturalSplits);
+                int s = 0;
+                for (int i = 0; i < naturalSplits; i++) {
+                    Spliterator<T> split = spliterator.split();
+                    int thisSplitSize = split.getSizeIfKnown();
+
+                    SizedCollectorTask<T> task = new SizedCollectorTask<>(this, split, offset + s, thisSplitSize);
+                    task.fork();
+
+                    s += thisSplitSize;
+                }
+
+                SizedCollectorTask<T> task = new SizedCollectorTask<>(this, spliterator, offset + s, length - s);
+                task.compute();
+            }
+        }
+    }
+
+    private static class ToArrayTask extends CountedCompleter<Void> {
+        private final int[] array;
+        private final IntNode node;
+        private final int offset;
+
+        private ToArrayTask(IntNode node, int[] array, int offset) {
+            this.array = array;
+            this.node = node;
+            this.offset = offset;
+        }
+
+        private ToArrayTask(ToArrayTask parent, IntNode node, int offset) {
+            super(parent);
+            this.array = parent.array;
+            this.node = node;
+            this.offset = offset;
+        }
+
+        @Override
+        public void compute() {
+            if (node.getChildCount() > 0) {
+                setPendingCount(node.getChildCount() - 1);
+
+                final Iterator<IntNode> itNodes = node.children();
+
+                // @@@ cast
+                final ToArrayTask firstTask = new ToArrayTask(this, itNodes.next(), offset);
+                int size = firstTask.node.size();
+
+                while (itNodes.hasNext()) {
+                    // @@@ cast
+                    final ToArrayTask task = new ToArrayTask(this, itNodes.next(), offset + size);
+                    size += task.node.size();
+                    task.fork();
+                }
+                firstTask.compute();
+            }
+            else {
+                // @@@ copy array
+                node.copyInto(array, offset);
+                tryComplete();
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/IntUnaryOperator.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.functions.UnaryOperator;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public interface IntUnaryOperator extends UnaryOperator<Integer> {
+
+    @Override
+    default Integer operate(Integer operand) {
+        Logger.getLogger(getClass().getName()).log(Level.WARNING, "{0} using boxed int", getClass().getName());
+        return operateInt(operand);
+    }
+
+    int operateInt(int operand);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/Primitives.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,721 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.*;
+import java.util.functions.Block;
+import java.util.functions.Factory;
+import java.util.functions.UnaryOperator;
+import java.util.logging.Logger;
+import java.util.streams.*;
+
+public final class Primitives {
+
+    private Primitives() {}
+
+    // Adapt iterator to int iterator
+
+    public static IntIterator adapt(final Iterator<Integer> i) {
+        if (i instanceof IntIterator) {
+            return (IntIterator) i;
+        }
+        else {
+            // @@@ throw UOE?
+            return new IntIterator() {
+                @Override
+                public boolean hasNext() {
+                    return i.hasNext();
+                }
+
+                @Override
+                public Integer next() {
+                    return i.next();
+                }
+
+                @Override
+                public int nextInt() {
+                    return i.next().intValue();
+                }
+            };
+        }
+    }
+
+    // Adapt sink to int sink
+
+    public static IntSink adapt(final Sink<Integer> s) {
+        if (s instanceof IntSink) {
+            return (IntSink) s;
+        }
+        else {
+            // @@@ throw UOE?
+            return new IntSink() {
+                @Override
+                public void begin(int size) {
+                    s.begin(size);
+                }
+
+                @Override
+                public void applyInt(int i) {
+                    s.apply(i);
+                }
+
+                @Override
+                public void end() {
+                    s.end();
+                }
+            };
+        }
+    }
+
+    // Adapt block to int block
+
+    public static IntBlock adapt(final Block<Integer> b) {
+        if (b instanceof IntBlock) {
+            return (IntBlock) b;
+        }
+        else {
+            // @@@ throw UOE?
+            return new IntBlock() {
+                @Override
+                public void applyInt(int i) {
+                    b.apply(i);
+                }
+            };
+        }
+    }
+
+    //
+
+    public static IntIterator emptyIntIterator() {
+        return EMPTY_INT_ITERATOR;
+    }
+
+    private static final IntIterator EMPTY_INT_ITERATOR = new IntIterator() {
+        @Override
+        public int nextInt() {
+            throw new NoSuchElementException();
+        }
+
+        @Override
+        public boolean hasNext() {
+            return false;
+        }
+
+        @Override
+        public void forEach(IntBlock sink) {
+        }
+    };
+
+    public static IntSpliterator emptyIntSpliterator() {
+        return EMPTY_INT_SPLITERATOR;
+    }
+
+    private static final IntSpliterator EMPTY_INT_SPLITERATOR = new IntSpliterator() {
+        @Override
+        public int getNaturalSplits() {
+            return 0;
+        }
+
+        @Override
+        public IntSpliterator split() {
+            return EMPTY_INT_SPLITERATOR;
+        }
+
+        @Override
+        public IntIterator iterator() {
+            return EMPTY_INT_ITERATOR;
+        }
+
+        @Override
+        public void forEach(IntBlock sink) {
+        }
+
+        @Override
+        public int getSizeIfKnown() {
+            return 0;
+        }
+
+        @Override
+        public boolean isPredictableSplits() {
+            return true;
+        }
+    };
+
+    public static IntIterator iterator(int[] array) {
+        return iterator(array, 0, array.length);
+    }
+
+    public static IntIterator iterator(int[] array, int offset, int length) {
+        // Validate bounds
+        return new IntArrayIterator(array, offset, length);
+    }
+
+    private static class IntArrayIterator implements IntIterator {
+        final int[] array;
+        int o;
+        final int e;
+
+        private IntArrayIterator(int[] array, int offset, int length) {
+            this.array = array;
+            this.o = offset;
+            this.e = offset + length;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return o < e;
+        }
+
+        @Override
+        public int nextInt() {
+            if (!hasNext()) {
+                throw new NoSuchElementException();
+            }
+
+            return array[o++];
+        }
+
+        @Override
+        public void forEach(IntBlock sink) {
+            if (hasNext()) {
+                for (int i = o; i < e; i++) {
+                    sink.applyInt(array[i]);
+                }
+                o = e;
+            }
+        }
+
+        @Override
+        public void forEach(Block<? super Integer> sink) {
+            IntIterator.super.forEach(sink);
+        }
+    }
+
+    public static IntIterator concat(final IntIterator i1, final IntIterator i2) {
+        Objects.requireNonNull(i1);
+        Objects.requireNonNull(i2);
+
+        return concat(Arrays.asList(i1, i2).iterator());
+    }
+
+    public static IntIterator concat(final Iterator<IntIterator> iterators) {
+        Objects.requireNonNull(iterators);
+
+        if (!iterators.hasNext())
+            return EMPTY_INT_ITERATOR;
+
+        return new IntIterator() {
+            private IntIterator it = Objects.requireNonNull(iterators.next());
+            // Need to retain a reference to the last iterator used with next()
+            // so that remove() can use that reference for deferral and check for two or more calls,
+            // and because hasNext() may update "it" to the next iterator
+            private IntIterator itForRemove = null;
+
+            @Override
+            public boolean hasNext() {
+                while (!it.hasNext()) {
+                    if (!iterators.hasNext()) {
+                        return false;
+                    }
+                    it = Objects.requireNonNull(iterators.next());
+                }
+
+                return true;
+            }
+
+            public int nextInt() {
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+
+                itForRemove = it;
+                return it.nextInt();
+            }
+
+            @Override
+            public void remove() {
+                if (itForRemove == null) {
+                    throw new IllegalStateException();
+                }
+
+                itForRemove.remove();
+                itForRemove = null;
+            }
+        };
+    }
+
+    // Infinite int streams
+
+    private static interface IntInfiniteIterator extends IntIterator {
+        @Override
+        default boolean hasNext() {
+            return true;
+        }
+    }
+
+    public static IntStream iterate(final int seed, final IntUnaryOperator f) {
+        Objects.requireNonNull(f);
+        final IntInfiniteIterator iterator = new IntInfiniteIterator() {
+            int t = seed;
+
+            @Override
+            public int nextInt() {
+                int v = t;
+                t = f.operateInt(t);
+                return v;
+            }
+        };
+        return stream(iterator, StreamOpFlags.IS_ORDERED);
+    }
+
+    public static IntStream repeat(int t) {
+        return repeat(-1, t);
+    }
+
+    public static IntStream repeat(final int n, final int t) {
+        return repeatedly(n, () -> t);
+    }
+
+    public static IntStream repeatedly(IntFactory f) {
+        return repeatedly(-1, f);
+    }
+
+    public static IntStream repeatedly(final int n, final IntFactory f) {
+        Objects.requireNonNull(f);
+
+        if (n < 0) {
+            IntInfiniteIterator iterator = () -> f.makeInt();
+            return stream(iterator, StreamOpFlags.IS_ORDERED);
+        }
+        else {
+            final IntIterator repeatedly = new IntIterator() {
+                int c = n;
+
+                @Override
+                public boolean hasNext() {
+                    return c > 0;
+                }
+
+                @Override
+                public int nextInt() {
+                    if (!hasNext()) {
+                        throw new NoSuchElementException();
+                    }
+
+                    c--;
+                    return f.makeInt();
+                }
+            };
+
+            return stream(repeatedly, StreamOpFlags.IS_ORDERED);
+        }
+    }
+
+    public static IntStream cycle(final IntIterable source) {
+        Objects.requireNonNull(source);
+
+        // Check if the source is empty
+        if (!source.iterator().hasNext()) {
+            return emptyIntStream();
+        }
+
+        final IntInfiniteIterator cycle = new IntInfiniteIterator() {
+            IntIterator i = source.iterator();
+
+            @Override
+            public int nextInt() {
+                if (!i.hasNext()) {
+                    i = source.iterator();
+                }
+
+                return i.nextInt();
+            }
+        };
+
+        return stream(cycle, StreamOpFlags.IS_ORDERED);
+    }
+
+
+    //
+
+    private static class IntArraySpliterator extends IntArrayIterator implements IntSpliterator {
+        private boolean traversing = false;
+
+        private IntArraySpliterator(int[] array, int offset, int length) {
+            super(array, offset, length);
+        }
+
+        @Override
+        public void forEach(Block<? super Integer> sink) {
+            traversing = true;
+            super.forEach(sink);
+        }
+
+        @Override
+        public void forEach(IntBlock sink) {
+            traversing = true;
+            super.forEach(sink);
+        }
+
+        @Override
+        public IntSpliterator split() {
+            if (traversing) {
+                throw new IllegalStateException("split after starting traversal");
+            }
+
+            int m = (e - o) / 2;
+            IntSpliterator ret = new IntArraySpliterator(array, o, m);
+            o += m;
+            return ret;
+        }
+
+        @Override
+        public IntIterator iterator() {
+            traversing = true;
+            return this;
+        }
+
+        @Override
+        public int getNaturalSplits() {
+            return (e - o > 1) ? 1 : 0;
+        }
+
+        @Override
+        public int getSizeIfKnown() {
+            return e - o;
+        }
+
+        @Override
+        public boolean isPredictableSplits() {
+            return true;
+        }
+    }
+
+    private static class ArrayIntSink implements IntSink {
+        private final int[] array;
+        private final int offset;
+        private int o;
+        private final int e;
+
+        ArrayIntSink(int[] array) {
+            this(array, 0, array.length);
+        }
+
+        ArrayIntSink(int[] array, int offset, int length) {
+            this.array = Objects.requireNonNull(array);
+            this.offset = offset;
+            this.o = offset;
+            this.e = offset + length;
+        }
+
+        @Override
+        public void begin(int size) {
+            o = offset;
+            if(size > (e - o)) {
+                Logger.getLogger(Primitives.class.getName()).warning(
+                        "Estimate greater than length. There might be blood.");
+            }
+        }
+
+        @Override
+        public void applyInt(int t) {
+            if (o >= e) {
+                throw new IndexOutOfBoundsException(Integer.toString(o));
+            }
+            array[o++] = t;
+        }
+    }
+
+    static class RangeIntIterator implements IntIterator {
+        int from;
+        int upTo;
+        int step;
+
+        public RangeIntIterator(int from, int upTo, int step) {
+            this.from = from;
+            this.upTo = upTo;
+            this.step = step;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return from < upTo;
+        }
+
+        @Override
+        public Integer next() {
+            // @@@ throw UOE
+            return nextInt();
+        }
+
+        @Override
+        public int nextInt() {
+            int r = from;
+            from += step;
+            return r;
+        }
+    }
+
+    static class RangeIntSpliterator extends RangeIntIterator implements IntSpliterator {
+        boolean traversing = false;
+
+        RangeIntSpliterator(int from, int upTo, int step) {
+            super(from, upTo, step);
+        }
+
+        @Override
+        public int getSizeIfKnown() {
+            return (upTo - from) / step;
+        }
+
+        @Override
+        public int getNaturalSplits() {
+            return (getSizeIfKnown() > 1) && !traversing ? 1 : 0;
+        }
+
+        @Override
+        public boolean isPredictableSplits() {
+            return true;
+        }
+
+        @Override
+        public IntSpliterator split() {
+            if (traversing) {
+                throw new IllegalStateException("split after starting traversal");
+            }
+
+            if (getSizeIfKnown() > 1) {
+                int mid = midPoint();
+                RangeIntSpliterator ret = new RangeIntSpliterator(from, from + mid, step);
+                from += mid;
+
+                return ret;
+            } else {
+                return EMPTY_INT_SPLITERATOR;
+            }
+        }
+
+        private int midPoint() {
+            if (step == 1) {
+                return (upTo - from) / 2;
+            } else {
+                int bisection = (upTo - from) / 2;
+                int remainder = (bisection - from) % step;
+
+                int midPoint = bisection - remainder;
+                if (midPoint <= from) {
+                    midPoint = bisection + (step - remainder);
+                }
+                return midPoint;
+            }
+        }
+
+        @Override
+        public IntIterator iterator() {
+            traversing = true;
+            return this;
+        }
+
+        @Override
+        public void forEach(Block<? super Integer> sink) {
+            traversing = true;
+            if (sink instanceof IntBlock) {
+                forEach((IntBlock) sink);
+            }
+            else {
+                super.forEach(sink);
+            }
+        }
+
+        @Override
+        public void forEach(IntBlock sink) {
+            traversing = true;
+            super.forEach(sink);
+        }
+    }
+
+    // Create IntStream from range
+
+    public static IntStream range(final int from, final int upTo) {
+        return range(from, upTo, 1);
+    }
+
+    public static IntStream range(final int from, final int upTo, final int step) {
+        IntSpliterator s = new RangeIntSpliterator(from, upTo, step);
+        return stream(s, StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED);
+    }
+
+    // Create parallel IntStream from range
+
+    public static IntStream parRange(final int from, final int upTo) {
+        return parRange(from, upTo, 1);
+    }
+
+    public static IntStream parRange(final int from, final int upTo, final int step) {
+        IntSpliterator s = new RangeIntSpliterator(from, upTo, step);
+        return stream(s, StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED | StreamOpFlags.IS_PARALLEL);
+    }
+
+    // Create Spliterator from arrays
+
+    public static IntSpliterator spliterator(int[] array) {
+        return spliterator(array, 0, array.length);
+    }
+
+    public static IntSpliterator spliterator(int[] array, int offset, int length) {
+        return new IntArraySpliterator(array, offset, length);
+    }
+
+    // Create Sink from arrays
+
+    public static IntSink sink(int[] array) {
+        return sink(array, 0, array.length);
+    }
+
+    public static IntSink sink(int[] array, int offset, int length) {
+        return new ArrayIntSink(array, offset, length);
+    }
+
+    // Create IntStream from IntIterable and IntIterator
+
+    public static<T extends Sized & IntIterable> IntStream stream(T entity, int flags) {
+        return toStream(new IntSpliterator.Sequential() {
+            @Override
+            public IntIterator iterator() {
+                return entity.iterator();
+            }
+
+            @Override
+            public void forEach(IntBlock block) {
+                entity.forEach(block);
+            }
+
+            @Override
+            public int getSizeIfKnown() {
+                return entity.size();
+            }
+        }, StreamOpFlags.IS_SIZED | flags);
+    }
+
+    public static IntStream stream(IntIterable entity, Sized sizeProvider, int flags) {
+        return toStream(new IntSpliterator.Sequential() {
+            @Override
+            public IntIterator iterator() {
+                return entity.iterator();
+            }
+
+            @Override
+            public void forEach(IntBlock block) {
+                entity.forEach(block);
+            }
+
+            @Override
+            public int getSizeIfKnown() {
+                return sizeProvider.size();
+            }
+        }, StreamOpFlags.IS_SIZED | flags);
+    }
+
+    public static IntStream stream(IntIterable entity, int flags) {
+        return toStream(new IntSpliterator.Sequential() {
+            @Override
+            public IntIterator iterator() {
+                return entity.iterator();
+            }
+
+            @Override
+            public void forEach(IntBlock block) {
+                entity.forEach(block);
+            }
+            // @@@ Mask off sized if set ?
+        }, flags);
+    }
+
+    public static IntStream stream(IntIterator entity, int flags) {
+        return toStream(new IntSpliterator.Sequential() {
+            @Override
+            public IntIterator iterator() {
+                return entity;
+            }
+
+            @Override
+            public void forEach(IntBlock block) {
+                entity.forEach(block);
+            }
+            // @@@ Mask off sized if set ?
+        }, flags);
+    }
+
+    // Create IntStream from array
+
+    public static IntStream stream(int[] source) {
+        return stream(source, 0, source.length);
+    }
+
+    public static IntStream stream(int[] source, int offset, int length) {
+        // Note use of full-service Spliterator here -- harmless because PARALLEL flag is not set
+        return toStream(spliterator(source, offset, length),
+                        StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED);
+    }
+
+    // Create IntStream from IntSpliterator
+
+    public static IntStream stream(IntSpliterator spliterator, int flags) {
+        if (spliterator.getSizeIfKnown() >= 0)
+            flags |= StreamOpFlags.IS_SIZED;
+        return toStream(spliterator, flags);
+    }
+
+    // Create parallel IntStream from array
+
+    public static IntStream parallel(int[] source) {
+        return parallel(source, 0, source.length);
+    }
+
+    public static IntStream parallel(int[] source, int offset, int length) {
+        return toStream(spliterator(source, offset, length),
+                        StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED | StreamOpFlags.IS_PARALLEL);
+    }
+
+    // Create parallel IntStream from IntSpliterator
+
+    public static IntStream parallel(IntSpliterator spliterator, int flags) {
+        if (spliterator.getSizeIfKnown() >= 0)
+            flags |= StreamOpFlags.IS_SIZED;
+        return toStream(spliterator, flags | StreamOpFlags.IS_PARALLEL);
+    }
+
+    //
+
+    public static IntStream emptyIntStream() {
+        return stream(emptyIntSpliterator(), 0);
+    }
+
+    //
+
+    private static IntStream toStream(IntSpliterator sp, int flags) {
+        return new IntPipeline<>(sp, flags);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/streams/primitives/RefToIntMapOp.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.streams.primitives;
+
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.functions.IntMapper;
+import java.util.streams.Sink;
+import java.util.streams.StreamOpFlags;
+import java.util.streams.StreamShape;
+import java.util.streams.StreamShapeFactory;
+import java.util.streams.ops.IntermediateOp;
+
+public class RefToIntMapOp<T> implements IntermediateOp<T, Integer> {
+
+    private final IntMapper<? super T> mapper;
+
+    public RefToIntMapOp(IntMapper<? super T> mapper) {
+        this.mapper = Objects.requireNonNull(mapper);
+    }
+
+    @Override
+    public int getOpFlags() {
+        return StreamOpFlags.NOT_SORTED | StreamOpFlags.NOT_DISTINCT;
+    }
+
+    @Override
+    public StreamShape inputShape() { return StreamShapeFactory.REFERENCE; }
+
+    @Override
+    public StreamShape outputShape() { return StreamShapeFactory.INT_VALUE; }
+
+    @Override
+    public IntIterator wrapIterator(int flags, final Iterator<T> source) {
+        return iterator(source, mapper);
+    }
+
+    @Override
+    public Sink<T> wrapSink(int flags, Sink sink) {
+        Objects.requireNonNull(sink);
+
+        IntSink intSink = Primitives.adapt(sink);
+        return new Sink<T>() {
+            @Override
+            public void begin(int size) {
+                intSink.begin(size);
+            }
+
+            @Override
+            public void apply(T t) {
+                intSink.applyInt(mapper.map(t));
+            }
+
+            @Override
+            public void end() {
+                intSink.end();
+            }
+        };
+    }
+
+    public static <T> IntIterator iterator(final Iterator<T> source, IntMapper<? super T> mapper) {
+        Objects.requireNonNull(source);
+
+        return new IntIterator() {
+            @Override
+            public boolean hasNext() {
+                return source.hasNext();
+            }
+
+            @Override
+            public int nextInt() {
+                return mapper.map(source.next());
+            }
+        };
+    }
+
+}
--- a/test-ng/tests/org/openjdk/tests/java/util/LambdaTestHelpers.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/test-ng/tests/org/openjdk/tests/java/util/LambdaTestHelpers.java	Thu Nov 15 00:47:35 2012 -0500
@@ -26,11 +26,13 @@
 
 import java.util.*;
 import java.util.functions.*;
+import java.util.streams.Streamable;
 import java.util.streams.TerminalSink;
 import java.util.streams.Stream;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
 
 /**
  * LambdaTestHelpers -- assertion methods and useful objects for lambda test cases
@@ -207,14 +209,28 @@
     }
 
     public static<T> void assertContents(Iterator<T> actual, Iterator<T> expected) {
+        List<T> history = new ArrayList<>();
+
         while (expected.hasNext()) {
-            assertTrue(actual.hasNext());
-            T pT = actual.next();
-            T lT = expected.next();
+            if (!actual.hasNext()) {
+                List<T> expectedData = new ArrayList<>(history);
+                while (expected.hasNext())
+                    expectedData.add(expected.next());
+                fail(String.format("Premature end of data; expected=%s, found=%s", expectedData, history));
+            }
+            T a = actual.next();
+            T e = expected.next();
+            history.add(a);
 
-            assertEquals(pT, lT);
+            if (!Objects.equals(a, e))
+                fail(String.format("Data mismatch; preceding=%s, nextExpected=%s, nextFound=%s", history, e, a));
         }
-        assertTrue(!actual.hasNext());
+        if (actual.hasNext()) {
+            List<T> rest = new ArrayList<>();
+            while (actual.hasNext())
+                rest.add(actual.next());
+            fail(String.format("Unexpected data %s after %s", rest, history));
+        }
     }
 
     @SafeVarargs
@@ -223,6 +239,14 @@
         assertContents(actual, Arrays.asList(expected).iterator());
     }
 
+    public static void assertStreamContents(List<Integer> sourceData, UnaryOperator<Stream<Integer>> mapper, List<Integer> expected) {
+        Integer[] source = sourceData.toArray(new Integer[sourceData.size()]);
+
+        assertContents(mapper.operate(Arrays.stream(source)).iterator(), expected.iterator());
+        assertContents(mapper.operate(Arrays.parallel(source)).sequential().iterator(), expected.iterator());
+        assertContents(mapper.operate(Arrays.parallel(source)).sequential().into(new ArrayList<Integer>()).iterator(), expected.iterator());
+    }
+
     public static <T> boolean equalsContentsUnordered(Iterable<T> a, Iterable<T> b) {
         Set<T> sa = new HashSet<>();
         for (T t : a) {
@@ -284,7 +308,7 @@
     public static <T,V> V iteratorToStatefulSink(Iterator<? extends T> iterator, TerminalSink<? super T, ? extends V> sink) {
         sink.begin(-1);
         while(iterator.hasNext()) {
-            sink.accept(iterator.next());
+            sink.apply(iterator.next());
         }
         sink.end();
         return sink.getAndClearState();
--- a/test-ng/tests/org/openjdk/tests/java/util/streams/OpTestCase.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/test-ng/tests/org/openjdk/tests/java/util/streams/OpTestCase.java	Thu Nov 15 00:47:35 2012 -0500
@@ -25,6 +25,7 @@
 package org.openjdk.tests.java.util.streams;
 
 import org.openjdk.tests.java.util.LambdaTestHelpers;
+import org.openjdk.tests.java.util.streams.primitives.IntStreamIntermediateOpTestScenario;
 import org.testng.Assert;
 import org.testng.ITestContext;
 import org.testng.annotations.BeforeClass;
@@ -36,6 +37,7 @@
 import java.util.functions.Factory;
 import java.util.streams.*;
 import java.util.streams.ops.*;
+import java.util.streams.primitives.IntStream;
 
 /**
  * StreamOpTestCase
@@ -52,6 +54,7 @@
         // @@@ This only needs to be done once, consider using injection or an attribute on ITestContext
         testScenarios = new HashMap<>();
         testScenarios.put(Stream.class, Collections.unmodifiableSet(EnumSet.allOf(StreamIntermediateOpTestScenario.class)));
+        testScenarios.put(IntStream.class, Collections.unmodifiableSet(EnumSet.allOf(IntStreamIntermediateOpTestScenario.class)));
     }
 
     // Exercise intermediate operations
@@ -220,14 +223,14 @@
             if (test.isApplicable(ops)) {
                 b.before.apply(b.data);
 
-                NodeBuilder<U> resultBuilder = Nodes.makeVariableSizeBuilder();
+                NodeBuilder<U> resultBuilder = b.shape.makeNodeBuilder(-1);
                 resultBuilder.begin(-1);
                 test.run(b.data, resultBuilder, ops);
                 resultBuilder.end();
                 Node<U> result = resultBuilder.build();
 
                 assertTrue(b.getEqualator(test).test(result, refResult),
-                           b.data.toString() + " " + test + " " + Arrays.toString(ops));
+                           String.format("%s %s %s: %s != %s", b.data.toString(), test, Arrays.toString(ops), refResult, result));
 
                 b.after.apply(b.data);
             }
@@ -317,24 +320,24 @@
     //
 
     @SuppressWarnings({"rawtypes", "unchecked"})
-    static <T> AbstractPipeline<?, T> chain(AbstractPipeline upstream, IntermediateOp<?, T> op) {
+    public static <T> AbstractPipeline<?, T> chain(AbstractPipeline upstream, IntermediateOp<?, T> op) {
         return upstream.chain(op);
     }
 
     @SuppressWarnings({"rawtypes", "unchecked"})
-    static <U> U chain(AbstractPipeline pipe, TerminalOp<?, U> op) {
+    public static <U> U chain(AbstractPipeline pipe, TerminalOp<?, U> op) {
         return (U) pipe.pipeline(op);
     }
 
     @SuppressWarnings({"rawtypes", "unchecked"})
-    static AbstractPipeline<?, ?> chain(AbstractPipeline pipe, IntermediateOp... ops) {
+    public static AbstractPipeline<?, ?> chain(AbstractPipeline pipe, IntermediateOp... ops) {
         for (IntermediateOp op : ops)
             pipe = chain(pipe, op);
         return pipe;
     }
 
     @SuppressWarnings("rawtypes")
-    static <U> U chain(AbstractPipeline pipe, TerminalOp<?, U> terminal, IntermediateOp... ops) {
+    public static <U> U chain(AbstractPipeline pipe, TerminalOp<?, U> terminal, IntermediateOp... ops) {
         return chain(chain(pipe, ops), terminal);
     }
 
@@ -343,6 +346,7 @@
     @SuppressWarnings({"rawtypes", "unchecked"})
     public static interface TestData<T> extends Iterable<T>, Sized {
 
+        // @@@ This is not used, should it be removed?
         Spliterator<T> spliterator();
 
         //
--- a/test-ng/tests/org/openjdk/tests/java/util/streams/StreamIntermediateOpTestScenario.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/test-ng/tests/org/openjdk/tests/java/util/streams/StreamIntermediateOpTestScenario.java	Thu Nov 15 00:47:35 2012 -0500
@@ -34,6 +34,12 @@
 @SuppressWarnings({"rawtypes", "unchecked"})
 public enum StreamIntermediateOpTestScenario implements OpTestCase.IntermediateOpTestScenario {
 
+    STREAM_FOR_EACH(false) {
+        public <T> void run(OpTestCase.TestData<T> data, Block b, IntermediateOp[] ops) {
+            stream(data.seq(ops)).forEach(b);
+        }
+    },
+
     // Wrap as stream and into a list
     STREAM_INTO(false) {
         public <T> void run(OpTestCase.TestData<T> data, Block b, IntermediateOp[] ops) {
--- a/test-ng/tests/org/openjdk/tests/java/util/streams/ops/FilterOpTest.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/test-ng/tests/org/openjdk/tests/java/util/streams/ops/FilterOpTest.java	Thu Nov 15 00:47:35 2012 -0500
@@ -29,6 +29,7 @@
 import org.openjdk.tests.java.util.streams.StreamTestDataProvider;
 import org.testng.annotations.Test;
 
+import java.util.Arrays;
 import java.util.Iterators;
 import java.util.streams.ops.FilterOp;
 import java.util.streams.ops.Node;
@@ -58,6 +59,12 @@
         assertCountSum(countTo(10).stream().filter(pOdd), 5, 25);
         assertCountSum(countTo(10).stream().filter(pTrue), 10, 55);
         assertCountSum(countTo(10).stream().filter(pEven).filter(pOdd), 0, 0);
+
+        assertStreamContents(countTo(1000), s -> s.filter(pTrue), countTo(1000));
+        assertStreamContents(countTo(1000), s -> s.filter(pFalse), countTo(0));
+        assertStreamContents(countTo(1000), s -> s.filter(e -> e > 100), range(101, 1000));
+        assertStreamContents(countTo(1000), s -> s.filter(e -> e < 100), countTo(99));
+        assertStreamContents(countTo(1000), s -> s.filter(e -> e == 100), Arrays.asList(100));
     }
 
     @Test(dataProvider = "opArrays", dataProviderClass = StreamTestDataProvider.class)
--- a/test-ng/tests/org/openjdk/tests/java/util/streams/ops/FindFirstOpTest.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/test-ng/tests/org/openjdk/tests/java/util/streams/ops/FindFirstOpTest.java	Thu Nov 15 00:47:35 2012 -0500
@@ -29,6 +29,7 @@
 import org.openjdk.tests.java.util.streams.StreamTestDataProvider;
 import org.testng.annotations.Test;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Optional;
 import java.util.streams.ops.FilterOp;
@@ -52,14 +53,13 @@
     public void testFindFirst() {
         assertFalse(Collections.<Integer>emptySet().stream().findFirst().isPresent(), "no result");
         assertFalse(countTo(10).stream().filter(x -> x > 10).findFirst().isPresent(), "no result");
-        assertEquals(2, (int) countTo(10).stream().filter(pEven).findFirst().get(), "first even number is 2");
-    }
 
-    public void testFindFirstParallel() {
-        assertFalse(Collections.<Integer>emptySet().parallel().findFirst().isPresent(), "no result");
-        assertFalse(countTo(1000).parallel().filter(x -> x > 1000).findFirst().isPresent(), "no result");
-        assertEquals(999, (int) countTo(1000).parallel().filter(x -> x == 999).findFirst().get(), "999 is present");
-        assertEquals(2, (int) countTo(1000).parallel().filter(pEven).findFirst().get(), "first even number is 2");
+        assertStreamContents(countTo(1000), s-> Arrays.asList(new Integer[] { s.filter(pEven).findFirst().get() }).stream(), Arrays.asList(2));
+        assertStreamContents(countTo(1000), s-> Arrays.asList(new Integer[] { s.findFirst().get() }).stream(), Arrays.asList(1));
+        assertStreamContents(countTo(1000), s-> Arrays.asList(new Integer[] { s.filter(e -> e == 499).findFirst().get() }).stream(), Arrays.asList(499));
+        assertStreamContents(countTo(1000), s-> Arrays.asList(new Integer[] { s.filter(e -> e == 999).findFirst().get() }).stream(), Arrays.asList(999));
+        assertStreamContents(countTo(0), s-> Arrays.asList(new Integer[] { s.findFirst().orElse(-1) }).stream(), Arrays.asList(-1));
+        assertStreamContents(countTo(1000), s-> Arrays.asList(new Integer[] { s.filter(e -> e == 1499).findFirst().orElse(-1) }).stream(), Arrays.asList(-1));
     }
 
     @Test(dataProvider = "opArrays", dataProviderClass = StreamTestDataProvider.class)
--- a/test-ng/tests/org/openjdk/tests/java/util/streams/ops/FlatMapOpTest.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/test-ng/tests/org/openjdk/tests/java/util/streams/ops/FlatMapOpTest.java	Thu Nov 15 00:47:35 2012 -0500
@@ -30,6 +30,7 @@
 import org.testng.annotations.Test;
 
 import java.util.Arrays;
+import java.util.functions.Block;
 import java.util.functions.FlatMapper;
 import java.util.streams.Stream;
 import java.util.streams.ops.FlatMapOp;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/java/util/streams/ops/IntNodeTest.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.tests.java.util.streams.ops;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.functions.Mapper;
+import java.util.streams.ops.Node;
+import java.util.streams.primitives.*;
+
+import static org.testng.Assert.*;
+
+@Test
+public class IntNodeTest {
+
+    @DataProvider(name = "nodes")
+    public Object[][] createSizes() {
+        List<Object[]> params = new ArrayList<>();
+
+        for (int size : Arrays.asList(0, 1, 4, 15, 16, 17, 127, 128, 129, 1000)) {
+            int[] array = new int[size];
+            for (int i = 0; i < array.length; i++) {
+                array[i] = i;
+            }
+
+            List<Node<Integer>> nodes = new ArrayList<>();
+
+            nodes.add(IntNodes.node(array));
+
+            nodes.add(degenerateTree(Primitives.iterator(array)));
+
+            nodes.add(tree(toList(array), l -> IntNodes.node(toIntArray(l))));
+
+            nodes.add(fill(array, IntNodes.makeBuilder(array.length)));
+
+            nodes.add(fill(array, IntNodes.makeVariableSizeBuilder()));
+
+            for (int i = 0; i < nodes.size(); i++) {
+                params.add(new Object[]{array, nodes.get(i)});
+            }
+
+        }
+
+        return params.toArray(new Object[0][]);
+    }
+
+    List<Integer> toList(int[] a) {
+        List<Integer> l = new ArrayList<>();
+        for (int i : a) {
+            l.add(i);
+        }
+
+        return l;
+    }
+
+    int[] toIntArray(List<Integer> l) {
+        int[] a = new int[l.size()];
+
+        int i = 0;
+        for (Integer e : l) {
+            a[i++] = e;
+        }
+        return a;
+    }
+
+    IntNode fill(int[] array, IntNodeBuilder nb) {
+        nb.begin(array.length);
+        for (Integer i : array) {
+            nb.applyInt(i);
+        }
+        nb.end();
+        return nb.build();
+    }
+
+    IntNode degenerateTree(IntIterator it) {
+        if (!it.hasNext()) {
+            return IntNodes.node(new int[0]);
+        }
+
+        int i = it.nextInt();
+        if (it.hasNext()) {
+            return IntNodes.node(IntNodes.node(new int[]{i}), degenerateTree(it));
+        }
+        else {
+            return IntNodes.node(new int[]{i});
+        }
+    }
+
+    IntNode tree(List<Integer> l, Mapper<IntNode, List<Integer>> m) {
+        if (l.size() < 3) {
+            return m.map(l);
+        }
+        else {
+            return IntNodes.node(tree(l.subList(0, l.size() / 2), m), tree(l.subList(l.size() / 2, l.size()), m));
+        }
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testAsArray(int[] array, IntNode n) {
+        assertEquals(n.asIntArray(), array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testFlattenAsArray(int[] array, IntNode n) {
+        assertEquals(n.flatten().asIntArray(), array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testCopyTo(int[] array, IntNode n) {
+        int[] copy = new int[n.size()];
+        n.copyInto(copy, 0);
+
+        assertEquals(copy, array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testForEach(int[] array, IntNode n) {
+        List<Integer> l = new ArrayList<>(n.size());
+        n.forEach(e -> {
+            l.add(e);
+        });
+
+        assertEquals(l.toArray(), array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testIterator(int[] array, IntNode n) {
+        List<Integer> l = new ArrayList<>(n.size());
+        IntIterator it = n.iterator();
+        while (it.hasNext()) {
+            l.add(it.nextInt());
+        }
+
+        assertEquals(l.toArray(), array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testRootSpliterator(int[] array, IntNode n) {
+        List<Integer> l = new ArrayList<>(n.size());
+        IntIterator it = n.spliterator().iterator();
+        while (it.hasNext()) {
+            l.add(it.nextInt());
+        }
+
+        assertEquals(l.toArray(), array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testDepthOneSpliterator(int[] array, IntNode n) {
+        List<Integer> l = new ArrayList<>(n.size());
+
+        IntSpliterator s = n.spliterator();
+        for (int i = 0; i < s.getNaturalSplits(); i++) {
+            IntIterator it = s.split().iterator();
+            while (it.hasNext()) {
+                l.add(it.nextInt());
+            }
+        }
+
+        IntIterator it = s.iterator();
+        while (it.hasNext()) {
+            l.add(it.nextInt());
+        }
+
+        assertEquals(l.toArray(), array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testTwoSpliterator(int[] array, IntNode n) {
+        List<Integer> l = new ArrayList<>(n.size());
+
+        IntSpliterator s2 = n.spliterator();
+        IntSpliterator s1 = s2.split();
+
+        IntIterator it = s1.iterator();
+        while (it.hasNext()) {
+            l.add(it.nextInt());
+        }
+
+        it = s2.iterator();
+        while (it.hasNext()) {
+            l.add(it.nextInt());
+        }
+
+        assertEquals(l.toArray(), array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testSpliterator(int[] array, IntNode n) {
+        List<Integer> l = new ArrayList<>(n.size());
+        split(l, n.spliterator());
+
+        assertEquals(l.toArray(), array);
+    }
+
+    void split(List<Integer> l, IntSpliterator s) {
+        if (s.getNaturalSplits() == 0) {
+            IntSpliterator _s = s.split();
+            assertEquals(_s.getNaturalSplits(), 0);
+            assertFalse(_s.split().iterator().hasNext());
+
+            s.forEach(e -> {
+                l.add(e);
+            });
+        }
+        else {
+            int size = s.getSizeIfKnown();
+            for (int i = 0; i < s.getNaturalSplits(); i++) {
+                IntSpliterator _s = s.split();
+                split(l, _s);
+            }
+
+            assertTrue(s.getSizeIfKnown() < size);
+
+            split(l, s);
+        }
+    }
+
+}
--- a/test-ng/tests/org/openjdk/tests/java/util/streams/ops/LimitOpTest.java	Wed Nov 14 12:21:20 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,110 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.openjdk.tests.java.util.streams.ops;
-
-import org.openjdk.tests.java.util.streams.OpTestCase;
-import org.openjdk.tests.java.util.streams.StreamTestData;
-import org.openjdk.tests.java.util.streams.StreamTestDataProvider;
-import org.testng.annotations.Test;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.streams.ops.Node;
-import java.util.streams.ops.SliceOp;
-
-import static org.openjdk.tests.java.util.LambdaTestHelpers.*;
-
-
-@Test
-public class LimitOpTest extends OpTestCase {
-
-    public void testLimit() {
-        assertCountSum(countTo(0).stream().limit(4), 0, 0);
-        assertCountSum(countTo(2).stream().limit(4), 2, 3);
-        assertCountSum(countTo(4).stream().limit(4), 4, 10);
-        assertCountSum(countTo(8).stream().limit(4), 4, 10);
-
-        assertContents(Collections.<Integer>emptyList().stream().limit(0).iterator(),
-                       Collections.<Integer>emptyList().iterator());
-
-        assertContents(countTo(100).stream().limit(0).iterator(),
-                       Collections.<Integer>emptyList().iterator());
-
-        assertContents(countTo(100).stream().limit(10).iterator(),
-                       countTo(10).iterator());
-
-        assertContents(countTo(100).stream().limit(100).iterator(),
-                       countTo(100).iterator());
-
-        assertContents(countTo(100).stream().limit(200).iterator(),
-                       countTo(100).iterator());
-    }
-
-    public void testShortCircuit() {
-        for (int l : Arrays.asList(0, 10)) {
-            AtomicInteger ai = new AtomicInteger();
-            countTo(100).stream()
-                        .tee(i -> { ai.getAndIncrement(); })
-                        .limit(l).toArray();
-            assertEquals(ai.get(), l, "tee block was called too many times");
-        }
-    }
-
-    @Test(dataProvider = "opArrays", dataProviderClass = StreamTestDataProvider.class)
-    public void testOps(String name, StreamTestData<Integer> data) {
-        List<Integer> limits = Collections.unmodifiableList(sizes(data));
-
-        for (int l : limits) {
-            Node<Integer> sr = exerciseOps(data, new SliceOp<>(0, l));
-            assertTrue(sr.size() <= l,
-                       String.format("size of stream result not within limit of %d", l));
-        }
-
-        for (int l : limits) {
-            Node<Integer> sr = exerciseOps(data, new SliceOp<>(0, l), new SliceOp<>(0, l / 2));
-            assertTrue(sr.size() <= l / 2,
-                       String.format("size of stream result not within limit of %d", l / 2));
-        }
-    }
-
-    private List<Integer> sizes(StreamTestData<?> data) {
-        int size = data.size();
-        if (size < 4) {
-            return Arrays.asList(0, 1, 2, 3, 4, 6);
-        }
-        else {
-            return Arrays.asList(0, 1, size / 2, size - 1, size, size + 1, 2 * size);
-        }
-    }
-
-    public void testSequentialShortCircuit() {
-        List<Integer> l = countTo(10).parallel().sequential().limit(5).into(new ArrayList<Integer>());
-        assertEquals(l.size(), 5);
-        assertEquals(l.get(l.size() -1).intValue(), 5);
-    }
-}
--- a/test-ng/tests/org/openjdk/tests/java/util/streams/ops/MapOpTest.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/test-ng/tests/org/openjdk/tests/java/util/streams/ops/MapOpTest.java	Thu Nov 15 00:47:35 2012 -0500
@@ -30,6 +30,7 @@
 import org.testng.annotations.Test;
 
 import java.util.Iterators;
+import java.util.functions.Block;
 import java.util.streams.ops.MapOp;
 import java.util.streams.ops.Node;
 
@@ -58,6 +59,11 @@
         assertCountSum(countTo(0).stream().map(mDoubler), 0, 0);
         assertCountSum(countTo(10).stream().map(mDoubler), 10, 110);
         assertCountSum(countTo(10).stream().map(mDoubler).map(mDoubler), 10, 220);
+
+
+        assertStreamContents(countTo(0), s -> s.map(e -> e), countTo(0));
+        assertStreamContents(countTo(1000), s -> s.map(e -> e), countTo(1000));
+        assertStreamContents(countTo(1000), s -> s.map(e -> 1000+e), range(1001, 2000));
     }
 
     @Test(dataProvider = "opArrays", dataProviderClass = StreamTestDataProvider.class)
--- a/test-ng/tests/org/openjdk/tests/java/util/streams/ops/NodeBuilderTest.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/test-ng/tests/org/openjdk/tests/java/util/streams/ops/NodeBuilderTest.java	Thu Nov 15 00:47:35 2012 -0500
@@ -75,7 +75,7 @@
         NodeBuilder<Integer> nb = m.map(l.size());
         nb.begin(l.size());
         for (int i : l) {
-            nb.accept(i);
+            nb.apply(i);
         }
         nb.end();
 
@@ -105,7 +105,7 @@
         NodeBuilder<Integer> nb = m.map(l.size());
         nb.begin(l.size());
         for (int i : l) {
-            nb.accept(i);
+            nb.apply(i);
         }
         nb.end();
 
--- a/test-ng/tests/org/openjdk/tests/java/util/streams/ops/NodeTest.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/test-ng/tests/org/openjdk/tests/java/util/streams/ops/NodeTest.java	Thu Nov 15 00:47:35 2012 -0500
@@ -77,7 +77,7 @@
     Node<Integer> fill(Integer[] array, NodeBuilder<Integer> nb) {
         nb.begin(array.length);
         for (Integer i : array) {
-            nb.accept(i);
+            nb.apply(i);
         }
         nb.end();
         return nb.build();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/java/util/streams/ops/PrimitiveOpsTests.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.tests.java.util.streams.ops;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.streams.primitives.*;
+
+@Test
+public class PrimitiveOpsTests {
+
+    public void testSum() {
+        int sum = Primitives.range(1, 10).filter(i -> i % 2 == 0).sum();
+        Assert.assertEquals(sum, 20);
+    }
+
+    public void testMap() {
+        int sum = Primitives.range(1, 10).filter(i -> i % 2 == 0).map(i -> i * 2).sum();
+        Assert.assertEquals(sum, 40);
+    }
+
+    public void testParSum() {
+        int sum = Primitives.parRange(1, 10).filter(i -> i % 2 == 0).sum();
+        Assert.assertEquals(sum, 20);
+    }
+
+    public void testSumPull() {
+        IntStream s = Primitives.range(1, 10).filter(i -> i % 2 == 0);
+
+        IntIterator ii = s.iterator();
+        int i = ii.nextInt();
+
+        Assert.assertEquals(s.sum() + i, 20);
+    }
+
+    public void testTee() {
+        int[] teeSum = new int[1];
+        int sum = Primitives.range(1, 10).filter(i -> i % 2 == 0).tee(i -> { teeSum[0] = teeSum[0] + i; }).sum();
+        Assert.assertEquals(teeSum[0], sum);
+    }
+
+    public void testForEach() {
+        int[] sum = new int[1];
+        Primitives.range(1, 10).filter(i -> i % 2 == 0).forEach(i -> { sum[0] = sum[0] + i; });
+        Assert.assertEquals(sum[0], 20);
+    }
+
+    public void testParForEach() {
+        AtomicInteger ai = new AtomicInteger(0);
+        Primitives.parRange(1, 10).filter(i -> i % 2 == 0).forEach(i -> { ai.addAndGet(i); });
+        Assert.assertEquals(ai.get(), 20);
+    }
+
+    public void testBox() {
+        List<Integer> l = Primitives.parRange(1, 10).boxed().into(new ArrayList<Integer>());
+        int sum = l.stream().reduce(0, (a, b) -> a + b);
+        Assert.assertEquals(sum, 45);
+    }
+
+    public void testUnBox() {
+       int sum = Arrays.asList(1, 2, 3, 4, 5).stream().mapToInt(i -> i).sum();
+        Assert.assertEquals(sum, 15);
+    }
+
+    public void testToArray() {
+        {
+            int[] array =  Primitives.range(1, 10).map(i -> i * 2).toArray();
+            Assert.assertEquals(array, new int[] {2, 4, 6, 8, 10, 12, 14, 16, 18});
+        }
+
+        {
+            int[] array =  Primitives.parRange(1, 10).map(i -> i * 2).toArray();
+            Assert.assertEquals(array, new int[] {2, 4, 6, 8, 10, 12, 14, 16, 18});
+        }
+    }
+
+    public void testSort() {
+        Random r = new Random();
+
+        int[] content = Primitives.repeatedly(10, () -> r.nextInt(100)).toArray();
+        int[] sortedContent = content.clone();
+        Arrays.sort(sortedContent);
+
+        {
+            int[] array =  Primitives.stream(content).sorted().toArray();
+            Assert.assertEquals(array, sortedContent);
+        }
+
+        {
+            int[] array =  Primitives.parallel(content).sorted().toArray();
+            Assert.assertEquals(array, sortedContent);
+        }
+    }
+
+    public void testSortSort() {
+        Random r = new Random();
+
+        int[] content = Primitives.repeatedly(10, () -> r.nextInt(100)).toArray();
+        int[] sortedContent = content.clone();
+        Arrays.sort(sortedContent);
+
+        {
+            int[] array =  Primitives.stream(content).sorted().sorted().toArray();
+            Assert.assertEquals(array, sortedContent);
+        }
+
+        {
+            int[] array =  Primitives.parallel(content).sorted().sorted().toArray();
+            Assert.assertEquals(array, sortedContent);
+        }
+    }
+
+    public void testSequential() {
+
+        int[] expected = Primitives.range(1, 1000).toArray();
+
+        {
+            IntNodeBuilder nb = IntNodes.makeVariableSizeBuilder();
+            Primitives.range(1, 1000).sequential().forEach(nb);
+            Assert.assertTrue(Arrays.equals(expected, nb.build().asIntArray()));
+        }
+
+        {
+            IntNodeBuilder nb = IntNodes.makeVariableSizeBuilder();
+            Primitives.parRange(1, 1000).sequential().forEach(nb);
+            Assert.assertTrue(Arrays.equals(expected, nb.build().asIntArray()));
+        }
+    }
+
+    public void testLimit() {
+        int[] expected = Primitives.range(1, 10).toArray();
+
+        {
+            int[] actual = Primitives.iterate(1, i -> i + 1).limit(9).toArray();
+            Assert.assertTrue(Arrays.equals(expected, actual));
+        }
+
+        {
+            int[] actual = Primitives.parRange(1, 100).limit(9).toArray();
+            Assert.assertTrue(Arrays.equals(expected, actual));
+        }
+    }
+
+}
--- a/test-ng/tests/org/openjdk/tests/java/util/streams/ops/SkipOpTest.java	Wed Nov 14 12:21:20 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.openjdk.tests.java.util.streams.ops;
-
-import org.openjdk.tests.java.util.streams.OpTestCase;
-import org.openjdk.tests.java.util.streams.StreamTestData;
-import org.openjdk.tests.java.util.streams.StreamTestDataProvider;
-import org.testng.annotations.Test;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.streams.ops.Node;
-import java.util.streams.ops.SliceOp;
-
-import static org.openjdk.tests.java.util.LambdaTestHelpers.*;
-
-
-@Test
-public class SkipOpTest extends OpTestCase {
-
-    public void testSkip() {
-        assertCountSum(countTo(0).stream().skip(0), 0, 0);
-        assertCountSum(countTo(0).stream().skip(4), 0, 0);
-        assertCountSum(countTo(4).stream().skip(4), 0, 0);
-        assertCountSum(countTo(4).stream().skip(2), 2, 7);
-        assertCountSum(countTo(4).stream().skip(0), 4, 10);
-
-        assertContents(Collections.<Integer>emptyList().stream().skip(0).iterator(),
-                       Collections.<Integer>emptyList().iterator());
-
-        assertContents(Collections.<Integer>emptyList().stream().skip(10).iterator(),
-                       Collections.<Integer>emptyList().iterator());
-
-        assertContents(countTo(100).stream().skip(0).iterator(),
-                       countTo(100).stream().iterator());
-
-        assertContents(countTo(100).stream().skip(10).iterator(),
-                       range(11, 100).iterator());
-
-        assertContents(countTo(100).stream().skip(100).iterator(),
-                       Collections.<Integer>emptyList().iterator());
-
-        assertContents(countTo(100).stream().skip(200).iterator(),
-                       Collections.<Integer>emptyList().iterator());
-    }
-
-    @Test(dataProvider = "opArrays", dataProviderClass = StreamTestDataProvider.class)
-    public void testOps(String name, StreamTestData<Integer> data) {
-        List<Integer> skips = Collections.unmodifiableList(sizes(data));
-
-        for (int s : skips) {
-            Node<Integer> sr = exerciseOps(data, new SliceOp<>(s));
-            assertTrue(data.size() - sr.size() <= s,
-                       String.format("size of stream result not within skip limit of %d", s));
-        }
-
-        for (int s : skips) {
-            Node<Integer> sr = exerciseOps(data, new SliceOp<>(s), new SliceOp<>(s / 2));
-            assertTrue(data.size() - sr.size() <= s + s / 2,
-                       String.format("size of stream result not within skip limit of %d", s + s / 2));
-        }
-    }
-
-    @Test(dataProvider = "opArrays", dataProviderClass = StreamTestDataProvider.class)
-    public void testSkipLimitOps(String name, StreamTestData<Integer> data) {
-        List<Integer> skips = Collections.unmodifiableList(sizes(data));
-
-        for (int s : skips) {
-            Node<Integer> sr = exerciseOps(data, new SliceOp<>(s), new SliceOp<>(0, 10));
-            assertTrue(sr.size() <= 10,
-                       String.format("size of stream result not within limit of 10"));
-            sr = exerciseOps(data, new SliceOp<>(s, 10));
-            assertTrue(sr.size() <= 10,
-                       String.format("size of stream result not within limit of 10"));
-        }
-    }
-
-    private List<Integer> sizes(StreamTestData<?> data) {
-        int size = data.size();
-        if (size < 4) {
-            return Arrays.asList(0, 1, 2, 3, 4, 6);
-        }
-        else {
-            return Arrays.asList(0, 1, size / 2, size - 1, size, size + 1, 2 * size);
-        }
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/java/util/streams/ops/SliceOpTest.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.tests.java.util.streams.ops;
+
+import org.openjdk.tests.java.util.streams.OpTestCase;
+import org.openjdk.tests.java.util.streams.StreamTestData;
+import org.openjdk.tests.java.util.streams.StreamTestDataProvider;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.functions.Mapper;
+import java.util.functions.UnaryOperator;
+import java.util.streams.AbstractPipeline;
+import java.util.streams.Stream;
+import java.util.streams.Streamable;
+import java.util.streams.ops.Node;
+import java.util.streams.ops.NodeBuilder;
+import java.util.streams.ops.Nodes;
+import java.util.streams.ops.SliceOp;
+
+import static org.openjdk.tests.java.util.LambdaTestHelpers.*;
+import static org.openjdk.tests.java.util.LambdaTestHelpers.assertContents;
+import static org.openjdk.tests.java.util.LambdaTestHelpers.countTo;
+
+/**
+ * SliceOpTest
+ *
+ * @author Brian Goetz
+ */
+@Test
+public class SliceOpTest extends OpTestCase {
+
+
+    public void testSkip() {
+        assertCountSum(countTo(0).stream().skip(0), 0, 0);
+        assertCountSum(countTo(0).stream().skip(4), 0, 0);
+        assertCountSum(countTo(4).stream().skip(4), 0, 0);
+        assertCountSum(countTo(4).stream().skip(2), 2, 7);
+        assertCountSum(countTo(4).stream().skip(0), 4, 10);
+
+        assertCountSum(countTo(0).parallel().skip(0), 0, 0);
+        assertCountSum(countTo(0).parallel().skip(4), 0, 0);
+        assertCountSum(countTo(4).parallel().skip(4), 0, 0);
+        assertCountSum(countTo(4).parallel().skip(2), 2, 7);
+        assertCountSum(countTo(4).parallel().skip(0), 4, 10);
+
+        assertStreamContents(Collections.emptyList(), s -> s.skip(0), Collections.emptyList());
+        assertStreamContents(Collections.emptyList(), s -> s.skip(10), Collections.emptyList());
+
+        assertStreamContents(countTo(1), s -> s.skip(0), countTo(1));
+        assertStreamContents(countTo(1), s -> s.skip(1), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.skip(0), countTo(100));
+        assertStreamContents(countTo(100), s -> s.skip(10), range(11, 100));
+        assertStreamContents(countTo(100), s -> s.skip(100), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.skip(200), Collections.emptyList());
+    }
+
+    public void testLimit() {
+        assertCountSum(countTo(0).stream().limit(4), 0, 0);
+        assertCountSum(countTo(2).stream().limit(4), 2, 3);
+        assertCountSum(countTo(4).stream().limit(4), 4, 10);
+        assertCountSum(countTo(8).stream().limit(4), 4, 10);
+
+        assertCountSum(countTo(0).parallel().limit(4), 0, 0);
+        assertCountSum(countTo(2).parallel().limit(4), 2, 3);
+        assertCountSum(countTo(4).parallel().limit(4), 4, 10);
+        assertCountSum(countTo(8).parallel().limit(4), 4, 10);
+
+        assertStreamContents(Collections.emptyList(), s -> s.limit(0), Collections.emptyList());
+        assertStreamContents(Collections.emptyList(), s -> s.limit(10), Collections.emptyList());
+        assertStreamContents(countTo(1), s -> s.limit(0), Collections.emptyList());
+        assertStreamContents(countTo(1), s -> s.limit(1), countTo(1));
+        assertStreamContents(countTo(100), s -> s.limit(0), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.limit(10), countTo(10));
+        assertStreamContents(countTo(100), s -> s.limit(10).limit(10), countTo(10));
+        assertStreamContents(countTo(100), s -> s.limit(100), countTo(100));
+        assertStreamContents(countTo(100), s -> s.limit(100).limit(10), countTo(10));
+        assertStreamContents(countTo(100), s -> s.limit(200), countTo(100));
+    }
+
+    public void testSkipLimit() {
+        assertStreamContents(Collections.emptyList(), s -> s.skip(0).limit(0), Collections.emptyList());
+        assertStreamContents(Collections.emptyList(), s -> s.skip(0).limit(10), Collections.emptyList());
+        assertStreamContents(Collections.emptyList(), s -> s.skip(10).limit(0), Collections.emptyList());
+        assertStreamContents(Collections.emptyList(), s -> s.skip(10).limit(10), Collections.emptyList());
+
+        assertStreamContents(countTo(100), s -> s.skip(0).limit(100), countTo(100));
+        assertStreamContents(countTo(100), s -> s.skip(0).limit(10), countTo(10));
+        assertStreamContents(countTo(100), s -> s.skip(0).limit(0), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.skip(10).limit(100), range(11, 100));
+        assertStreamContents(countTo(100), s -> s.skip(10).limit(10), range(11, 20));
+        assertStreamContents(countTo(100), s -> s.skip(10).limit(0), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.skip(100).limit(100), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.skip(100).limit(10), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.skip(100).limit(0), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.skip(200).limit(100), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.skip(200).limit(10), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.skip(200).limit(0), Collections.emptyList());
+    }
+
+    public void testSlice() {
+        assertStreamContents(Collections.emptyList(), s -> s.slice(0, 0), Collections.emptyList());
+        assertStreamContents(Collections.emptyList(), s -> s.slice(0, 10), Collections.emptyList());
+        assertStreamContents(Collections.emptyList(), s -> s.slice(10, 0), Collections.emptyList());
+        assertStreamContents(Collections.emptyList(), s -> s.slice(10, 10), Collections.emptyList());
+
+        assertStreamContents(countTo(100), s -> s.slice(0, 100), countTo(100));
+        assertStreamContents(countTo(100), s -> s.slice(0, 10), countTo(10));
+        assertStreamContents(countTo(100), s -> s.slice(0, 0), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.slice(10, 100), range(11, 100));
+        assertStreamContents(countTo(100), s -> s.slice(10, 10), range(11, 20));
+        assertStreamContents(countTo(100), s -> s.slice(10, 0), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.slice(100, 100), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.slice(100, 10), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.slice(100, 0), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.slice(200, 100), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.slice(200, 10), Collections.emptyList());
+        assertStreamContents(countTo(100), s -> s.slice(200, 0), Collections.emptyList());
+    }
+
+    private int sliceSize(int dataSize, int skip, int limit) {
+        int size = Math.max(0, dataSize - skip);
+        if (limit >= 0)
+            size = Math.min(size, limit);
+        return size;
+    }
+
+    private int sliceSize(int dataSize, int skip) {
+        return Math.max(0, dataSize - skip);
+    }
+
+    @Test(dataProvider = "opArrays", dataProviderClass = StreamTestDataProvider.class)
+    public void testSkipOps(String name, StreamTestData<Integer> data) {
+        List<Integer> skips = Collections.unmodifiableList(sizes(data));
+
+        for (int s : skips) {
+            Node<Integer> sr = exerciseOps(data, new SliceOp<>(s));
+            assertEquals(sr.size(), sliceSize(data.size(), s));
+
+            sr = exerciseOps(data, new SliceOp<>(s), new SliceOp<>(s / 2));
+            assertEquals(sr.size(), sliceSize(sliceSize(data.size(), s), s/2));
+        }
+    }
+
+    @Test(dataProvider = "opArrays", dataProviderClass = StreamTestDataProvider.class)
+    public void testSkipLimitOps(String name, StreamTestData<Integer> data) {
+        List<Integer> skips = Collections.unmodifiableList(sizes(data));
+        List<Integer> limits = Collections.unmodifiableList(sizes(data));
+
+        for (int s : skips) {
+            for (int limit : limits) {
+                Node<Integer> sr = exerciseOps(data, new SliceOp<>(s), new SliceOp<>(0, limit));
+                assertEquals(sr.size(), sliceSize(sliceSize(data.size(), s), 0, limit));
+
+                sr = exerciseOps(data, new SliceOp<>(s, limit));
+                assertEquals(sr.size(), sliceSize(data.size(), s, limit));
+            }
+        }
+    }
+
+    @Test(dataProvider = "opArrays", dataProviderClass = StreamTestDataProvider.class)
+    public void testLimitOps(String name, StreamTestData<Integer> data) {
+        List<Integer> limits = Collections.unmodifiableList(sizes(data));
+
+        for (int limit : limits) {
+            Node<Integer> sr = null;
+            sr = exerciseOps(data, new SliceOp<>(0, limit));
+            assertEquals(sr.size(), sliceSize(data.size(), 0, limit));
+
+            sr = exerciseOps(data, new SliceOp<>(0, limit), new SliceOp<>(0, limit / 2));
+            assertEquals(sr.size(), sliceSize(sliceSize(data.size(), 0, limit), 0, limit/2));
+        }
+    }
+
+    public void testLimitShortCircuit() {
+        for (int l : Arrays.asList(0, 10)) {
+            AtomicInteger ai = new AtomicInteger();
+            countTo(100).stream()
+                    .tee(i -> { ai.getAndIncrement(); })
+                    .limit(l).toArray();
+            assertEquals(ai.get(), l, "tee block was called too many times");
+        }
+    }
+
+    public void testSkipParallel() {
+        List<Integer> l = countTo(1000).parallel().skip(200).limit(200).sequential().into(new ArrayList<Integer>());
+        assertEquals(l.size(), 200);
+        assertEquals(l.get(l.size() -1).intValue(), 400);
+    }
+
+    public void testLimitParallel() {
+        List<Integer> l = countTo(1000).parallel().limit(500).sequential().into(new ArrayList<Integer>());
+        assertEquals(l.size(), 500);
+        assertEquals(l.get(l.size() -1).intValue(), 500);
+    }
+
+    private List<Integer> sizes(StreamTestData<?> data) {
+        int size = data.size();
+        if (size < 4) {
+            return Arrays.asList(0, 1, 2, 3, 4, 6);
+        }
+        else {
+            return Arrays.asList(0, 1, size / 2, size - 1, size, size + 1, 2 * size);
+        }
+    }
+}
--- a/test-ng/tests/org/openjdk/tests/java/util/streams/ops/ToArrayOpTest.java	Wed Nov 14 12:21:20 2012 -0500
+++ b/test-ng/tests/org/openjdk/tests/java/util/streams/ops/ToArrayOpTest.java	Thu Nov 15 00:47:35 2012 -0500
@@ -122,7 +122,7 @@
         {
             NodeBuilder<Integer> nodeBuilder = Nodes.makeBuilder(l.size());
             for (Integer i : l) {
-                nodeBuilder.accept(i);
+                nodeBuilder.apply(i);
             }
             Object[] output = Streams.stream(nodeBuilder, StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED).toArray();
             assertEquals(Arrays.asList(output), l);
@@ -132,7 +132,7 @@
             NodeBuilder<Integer> nodeBuilder = Nodes.makeVariableSizeBuilder();
             nodeBuilder.begin(l.size());
             for (Integer i : l) {
-                nodeBuilder.accept(i);
+                nodeBuilder.apply(i);
             }
             nodeBuilder.end();
             Object[] output = Streams.stream(nodeBuilder, StreamOpFlags.IS_SIZED | StreamOpFlags.IS_ORDERED).toArray();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/java/util/streams/primitives/IntFilterOpTest.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.tests.java.util.streams.primitives;
+
+import org.openjdk.tests.java.util.streams.OpTestCase;
+import org.testng.annotations.Test;
+
+import java.util.streams.ops.Node;
+import java.util.streams.primitives.IntFilterOp;
+
+@Test
+public class IntFilterOpTest extends OpTestCase {
+
+    @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class)
+    public void testOps(String name, IntStreamTestData data) {
+        Node<Integer> result = exerciseOps(data, new IntFilterOp(i -> true));
+        assertEquals(result.size(), data.size());
+
+        result = exerciseOps(data, new IntFilterOp(i -> false));
+        assertEquals(result.size(), 0);
+
+        exerciseOps(data, new IntFilterOp(i -> 0 == i % 2));
+        exerciseOps(data, new IntFilterOp(i -> 1 == i % 2));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/java/util/streams/primitives/IntStreamIntermediateOpTestScenario.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.tests.java.util.streams.primitives;
+
+import org.openjdk.tests.java.util.streams.OpTestCase;
+
+import java.util.Iterator;
+import java.util.functions.Block;
+import java.util.streams.*;
+import java.util.streams.ops.FlagDeclaringOp;
+import java.util.streams.ops.IntermediateOp;
+import java.util.streams.primitives.IntBlock;
+import java.util.streams.primitives.IntIterator;
+import java.util.streams.primitives.IntStream;
+import java.util.streams.primitives.Primitives;
+
+@SuppressWarnings({"rawtypes", "unchecked"})
+public enum IntStreamIntermediateOpTestScenario implements OpTestCase.IntermediateOpTestScenario {
+
+    STREAM_FOR_EACH(false) {
+        public void run(IntStreamTestData data, IntBlock b, IntermediateOp[] ops) {
+            stream(data.seq(ops)).forEach(b);
+        }
+    },
+
+    STREAM_TO_ARRAY(false) {
+        public void run(IntStreamTestData data, IntBlock b, IntermediateOp[] ops) {
+            for (int t : stream(data.seq(ops)).toArray()) {
+                b.applyInt(t);
+            }
+        }
+    },
+
+    STREAM_ITERATOR(false) {
+        public void run(IntStreamTestData data, IntBlock b, IntermediateOp[] ops) {
+            for (IntIterator seqIter = stream(data.seq(ops)).iterator(); seqIter.hasNext(); )
+                b.applyInt(seqIter.nextInt());
+        }
+    },
+
+    STREAM_MIXED(false) {
+        public void run(IntStreamTestData data, IntBlock b, IntermediateOp[] ops) {
+            IntStream stream = stream(data.seq(ops));
+            IntIterator iter = stream.iterator();
+            if (iter.hasNext())
+                b.applyInt(iter.nextInt());
+            stream.forEach(b);
+        }
+    },
+
+    STREAM_MIXED_ITERATOR_FOR_EACH(false) {
+        public void run(IntStreamTestData data, IntBlock b, IntermediateOp[] ops) {
+            AbstractPipeline<?, ?> pipe1 = data.seq(new NoOp());
+            AbstractPipeline<?, ?> pipe2 = OpTestCase.chain(pipe1, ops);
+
+            pipe1.iterator();
+            stream(pipe2).forEach(b);
+        }
+    },
+
+    PAR_STREAM_SEQUENTIAL_FOR_EACH(true) {
+        public void run(IntStreamTestData data, IntBlock b, IntermediateOp[] ops) {
+            stream(data.par(ops)).sequential().forEach(b);
+        }
+    },
+
+    PAR_STREAM_TO_ARRAY(true) {
+        public void run(IntStreamTestData data, IntBlock b, IntermediateOp[] ops) {
+            for (int t : stream(data.par(ops)).toArray())
+                b.applyInt(t);
+        }
+    },
+
+    PAR_STREAM_TO_ARRAY_CLEAR_SIZED(true) {
+        public void run(IntStreamTestData data, IntBlock b, IntermediateOp[] ops) {
+            AbstractPipeline<?, ?> pipe1 = data.seq(new FlagDeclaringOp(StreamOpFlags.NOT_SIZED) {
+                @Override
+                public StreamShape outputShape() {
+                    return StreamShapeFactory.INT_VALUE;
+                }
+
+                @Override
+                public StreamShape inputShape() {
+                    return StreamShapeFactory.INT_VALUE;
+                }
+            });
+            AbstractPipeline<?, ?> pipe2 = OpTestCase.chain(pipe1, ops);
+
+            for (int t : stream(pipe2).toArray())
+                b.applyInt(t);
+        }
+    },
+
+    PAR_STREAM_ITERATOR_TO_ARRAY_MIXED(true) {
+        public void run(IntStreamTestData data, IntBlock b, IntermediateOp[] ops) {
+            IntStream stream = stream(data.par(ops));
+            IntIterator iter = stream.iterator();
+            if (iter.hasNext())
+                b.applyInt(iter.nextInt());
+            for (int t : stream.toArray())
+                b.applyInt(t);
+        }
+    },
+
+    PAR_STREAM_MIXED_ITERATOR_TO_ARRAY(true) {
+        public void run(IntStreamTestData data, IntBlock b, IntermediateOp[] ops) {
+            AbstractPipeline<?, ?> pipe1 = data.par(new NoOp());
+            AbstractPipeline<?, ?> pipe2 = OpTestCase.chain(pipe1, ops);
+
+            pipe1.iterator();
+            for (int t : stream(pipe2).toArray())
+                b.applyInt(t);
+        }
+    },
+
+    // Wrap as parallel stream, and iterate in mixed mode
+    PAR_STREAM_SEQUENTIAL_MIXED(true) {
+        public void run(IntStreamTestData data, IntBlock b, IntermediateOp[] ops) {
+            IntStream stream = stream(data.par(ops));
+            IntIterator iter = stream.iterator();
+            if (iter.hasNext())
+                b.applyInt(iter.nextInt());
+            stream.sequential().forEach(b);
+        }
+    },
+
+    ;
+
+    private static class NoOp implements IntermediateOp<Integer, Integer> {
+        @Override
+        public StreamShape inputShape() {
+            return StreamShapeFactory.INT_VALUE;
+        }
+
+        @Override
+        public StreamShape outputShape() {
+            return StreamShapeFactory.INT_VALUE;
+        }
+
+        @Override
+        public Iterator<Integer> wrapIterator(int flags, Iterator<Integer> in) {
+            return in;
+        }
+
+        @Override
+        public Sink<Integer> wrapSink(int flags, Sink<Integer> sink) {
+            return sink;
+        }
+    }
+
+    private boolean isParallel;
+
+    IntStreamIntermediateOpTestScenario(boolean isParallel) {
+        this.isParallel = isParallel;
+    }
+
+    public StreamShape getShape() {
+        return StreamShapeFactory.INT_VALUE;
+    }
+
+    public boolean isParallel() {
+        return isParallel;
+    }
+
+    public <T> void run(OpTestCase.TestData<T> data, Block b, IntermediateOp[] ops) {
+        run((IntStreamTestData) data, Primitives.adapt(b), ops);
+    }
+
+    public abstract <T> void run(IntStreamTestData data, IntBlock b, IntermediateOp[] ops);
+
+    IntStream stream(AbstractPipeline<?, ?> ap) {
+        return (IntStream) ap;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/java/util/streams/primitives/IntStreamTestData.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.tests.java.util.streams.primitives;
+
+import org.openjdk.tests.java.util.streams.OpTestCase;
+
+import java.util.streams.AbstractPipeline;
+import java.util.streams.StreamShape;
+import java.util.streams.StreamShapeFactory;
+import java.util.streams.primitives.IntIterator;
+import java.util.streams.primitives.IntSpliterator;
+import java.util.streams.primitives.IntStream;
+import java.util.streams.primitives.Primitives;
+
+public abstract class IntStreamTestData implements OpTestCase.TestData<Integer> {
+
+    @Override
+    @SuppressWarnings("rawtypes")
+    public StreamShape getShape() {
+        return StreamShapeFactory.INT_VALUE;
+    }
+
+    @SuppressWarnings("unchecked")
+    public IntStream seqStream() {
+        return (IntStream) seq();
+    }
+
+    @SuppressWarnings("unchecked")
+    public IntStream parStream() {
+        return (IntStream) par();
+    }
+
+    public static class ArrayData extends IntStreamTestData {
+        private final String name;
+        private final int[] array;
+
+        public ArrayData(String name, int[] array) {
+            this.name = name;
+            this.array = array;
+        }
+
+        @Override
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        public AbstractPipeline<?, Integer> seq() {
+            return (AbstractPipeline<?, Integer>) Primitives.stream(array);
+        }
+
+        @Override
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        public AbstractPipeline<?, Integer> par() {
+            return (AbstractPipeline<?, Integer>) Primitives.parallel(array);
+        }
+
+        @Override
+        public IntIterator iterator() {
+            return Primitives.iterator(array);
+        }
+
+        @Override
+        public IntSpliterator spliterator() {
+            return Primitives.spliterator(array);
+        }
+
+        @Override
+        public int size() {
+            return array.length;
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/java/util/streams/primitives/IntStreamTestDataProvider.java	Thu Nov 15 00:47:35 2012 -0500
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.tests.java.util.streams.primitives;
+
+import org.openjdk.tests.java.util.LambdaTestHelpers;
+import org.testng.annotations.DataProvider;
+
+import java.util.*;
+import java.util.functions.Combiner;
+
+public class IntStreamTestDataProvider {
+    private static final int[] to0 = new int[0];
+    private static final int[] to1 = new int[1];
+    private static final int[] to10 = new int[10];
+    private static final int[] to100 = new int[100];
+    private static final int[] to1000 = new int[1000];
+    private static final int[] reversed = new int[100];
+    private static final int[] ones = new int[100];
+    private static final int[] twice = new int[200];
+    private static final int[] pseudoRandom;
+
+    private static final Object[][] testData;
+
+    static {
+        int[][] arrays = {to0, to1, to10, to100, to1000};
+        for (int[] arr : arrays) {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = i;
+            }
+        }
+        for (int i = 0; i < reversed.length; i++) {
+            reversed[i] = reversed.length - i;
+        }
+        for (int i = 0; i < ones.length; i++) {
+            ones[i] = 1;
+        }
+        System.arraycopy(to100, 0, twice, 0, to100.length);
+        System.arraycopy(to100, 0, twice, to100.length, to100.length);
+        pseudoRandom = new int[LambdaTestHelpers.LONG_STRING.length()];
+        for (int i = 0; i < LambdaTestHelpers.LONG_STRING.length(); i++) {
+            pseudoRandom[i] = (int) LambdaTestHelpers.LONG_STRING.charAt(i);
+        }
+    }
+
+    static final Object[][] arrays = {
+            {"empty", to0},
+            {"0..1", to1},
+            {"0..10", to10},
+            {"0..100", to100},
+            {"0..1000", to1000},
+            {"100x[1]", ones},
+            {"2x[0..100]", twice},
+            {"reverse 0..100", reversed},
+            {"pseudorandom", pseudoRandom}
+    };
+
+    static {
+        List<Object[]> list = new ArrayList<>();
+        for (Object[] data : arrays) {
+            final Object name = data[0];
+            final int[] ints = (int[])data[1];
+
+            list.add(e("array:" + name, ints, IntStreamTestData.ArrayData::new));
+        }
+        testData = list.toArray(new Object[0][]);
+    }
+
+    static <T> Object[] e(String description, int[] data, Combiner<IntStreamTestData, String, int[]> m) {
+        return new Object[] { description, m.combine(description, data) };
+    }
+
+    // Return an array of ( String name, IntTestData<Integer> )
+    @DataProvider(name = "IntStreamTestData")
+    public static Object[][] makeValueTestData() {
+        return testData;
+    }
+}