/* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/licenses/publicdomain */ package jsr166y.forkjoin; import java.util.concurrent.locks.*; import java.util.concurrent.atomic.*; /** * A variant of a cyclic barrier that is advanced upon explicit * signals representing event occurrences. */ final class PoolBarrier { /** * Wait nodes for Treiber stack representing wait queue. */ static final class QNode { QNode next; volatile Thread thread; // nulled to cancel wait final long count; QNode(long c) { count = c; thread = Thread.currentThread(); } final boolean signal() { Thread t = thread; if (t == null) return false; thread = null; LockSupport.unpark(t); return true; } } /** * The event count */ final AtomicLong counter = new AtomicLong(); /** * Head of Treiber stack. Even though this variable is very * busy, it is not usually heavily contended because of * signal/wait/release policies. */ final AtomicReference<QNode> head = new AtomicReference<QNode>(); /** * Returns the current event count */ final long getCount() { return counter.get(); } /** * Waits until event count advances from count, or some thread is * waiting on a previous count. Help wake up others on release. * @param prev previous value returned by sync (or 0) * @return current event count */ final long sync(long prev) { long count = counter.get(); if (count != prev || enqAndWait(count)) releaseAll(); return count; } /** * Increment event count and release waiting threads. */ final void signal() { counter.incrementAndGet(); releaseAll(); } /** * Try to increment event count and release a waiting thread, if * one exists (released threads will in turn wake up * others). Allows repeated invocation by caller to recheck need * for signal on contention. * @return true if successful */ final boolean trySignal() { QNode q; final AtomicReference<QNode> hd = this.head; final AtomicLong ctr = this.counter; long c = ctr.get(); boolean inc = ctr.compareAndSet(c, c+1); // Even if CAS fails, we know that count is now > c, so help release while ((q = hd.get()) != null && q.count <= c) { if (hd.compareAndSet(q, q.next) && q.signal()) break; } return inc; } /** * Enqueue, block and wait for signal * @return true if counter advanced, else false (on spurious wakeup) */ private final boolean enqAndWait(long count) { final AtomicReference<QNode> hd = this.head; final AtomicLong ctr = this.counter; QNode node = null; // delay construction until first check QNode h; while (((h = hd.get()) == null || h.count == count) && ctr.get() == count) { if (node == null) node = new QNode(count); else if (hd.compareAndSet(node.next = h, node)) { while (!Thread.interrupted() && node.thread != null && ctr.get() == count) LockSupport.park(this); node.thread = null; if (ctr.get() == count) // premature wake up return false; // don't release others below break; } } return true; } /** * Release all waiting threads. Called on exit from sync, as well * as on contention in signal. Regardless of why sync'ing threads * exit, other waiting threads must also recheck for tasks or * completions before resync. Release by chopping off entire list, * and then signalling. This both lessens contention and avoids * unbounded enq/deq races. */ private final void releaseAll() { AtomicReference<QNode> hd = this.head; QNode q; while ( (q = hd.get()) != null) { if (hd.compareAndSet(q, null)) { do { q.signal(); } while ((q = q.next) != null); break; } } } }