/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package bes.concurrent; import java.util.Iterator; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.concurrent.locks.LockSupport; /** * <p>A relatively easy to use utility for general purpose thread signalling.</p> * <p>Usage on a thread awaiting a state change using a WaitQueue q is:</p> * <pre> * {@code * while (!conditionMet()) * Signal s = q.register(); * if (!conditionMet()) // or, perhaps more correctly, !conditionChanged() * s.await(); * else * s.cancel(); * } * </pre> * A signalling thread, AFTER changing the state, then calls q.signal() to wake up one, or q.signalAll() * to wake up all, waiting threads. * <p>To understand intuitively how this class works, the idea is simply that a thread, once it considers itself * incapable of making progress, registers to be awoken once that changes. Since this could have changed between * checking and registering (in which case the thread that made this change would have been unable to signal it), * it checks the condition again, sleeping only if it hasn't changed/still is not met.</p> * <p>This thread synchronisation scheme has some advantages over Condition objects and Object.wait/notify in that no monitor * acquisition is necessary and, in fact, besides the actual waiting on a signal, all operations are non-blocking. * As a result consumers can never block producers, nor each other, or vice versa, from making progress. * Threads that are signalled are also put into a RUNNABLE state almost simultaneously, so they can all immediately make * progress without having to serially acquire the monitor/lock, reducing scheduler delay incurred.</p> * * <p>A few notes on utilisation:</p> * <p>1. A thread will only exit await() when it has been signalled, but this does not guarantee the condition has not * been altered since it was signalled, and depending on your design it is likely the outer condition will need to be * checked in a loop, though this is not always the case.</p> * <p>2. Each signal is single use, so must be re-registered after each await(). This is true even if it times out.</p> * <p>3. If you choose not to wait on the signal (because the condition has been met before you waited on it) * you must cancel() the signal if the signalling thread uses signal() to awake waiters; otherwise signals will be * lost. If signalAll() is used but infrequent, and register() is frequent, cancel() should still be used to prevent the * queue growing unboundedly. Similarly, if you provide a TimerContext, cancel should be used to ensure it is not erroneously * counted towards wait time.</p> * <p>4. Care must be taken when selecting conditionMet() to ensure we are waiting on the condition that actually * indicates progress is possible. In some complex cases it may be tempting to wait on a condition that is only indicative * of local progress, not progress on the task we are aiming to complete, and a race may leave us waiting for a condition * to be met that we no longer need. * <p>5. This scheme is not fair</p> * <p>6. Only the thread that calls register() may call await()</p> */ public final class WaitQueue { private static final int CANCELLED = -1; private static final int SIGNALLED = 1; private static final int NOT_SET = 0; private volatile RegisteredSignal head, tail; public WaitQueue() { head = tail = new RegisteredSignal(); head.cancel(); } /** * The calling thread MUST be the thread that uses the signal * @return x */ public Signal register() { RegisteredSignal insert = new RegisteredSignal(); RegisteredSignal tail = this.tail; while (true) { RegisteredSignal next = tail.next; if (next != null) { tail = next; } else { insert.prev = tail; if (nextUpdater.compareAndSet(tail, null, insert)) { // no need for atomicity; if updates are out of order, chain simply needs to be walked forwards this.tail = insert; return insert; } else { tail = tail.next; } } } } /** * Signal one waiting thread */ public boolean signal() { RegisteredSignal ph = head, h = ph; while (true) { RegisteredSignal n = h.next; // we set prev to null simply to help out gc if (n == null || n.signal() != null) { // no need for atomicity; if update is slow, head is simply in the past and will have to be walked forwards if (ph != h) head = h; return n != null; } prevUpdater.lazySet(n, null); h = n; } } public boolean hasWaiters() { RegisteredSignal ph = head, h = ph; while (true) { RegisteredSignal n = h.next; if (n == null || !n.isSet()) { if (ph != h) head = h; return n != null; } prevUpdater.lazySet(n, null); h = n; } } /** * Signal all waiting threads */ public void signalAll() { RegisteredSignal h = head; RegisteredSignal t = tail; while (true) { if (h == t) return; RegisteredSignal n = t.next; if (n == null) break; prevUpdater.lazySet(n, null); t = n; } head = t; while (h != t) { RegisteredSignal n = h.next; n.signal(); h = n; } } /** * A Signal is a one-time-use mechanism for a thread to wait for notification that some condition * state has transitioned that it may be interested in (and hence should check if it is). * It is potentially transient, i.e. the state can change in the meantime, it only indicates * that it should be checked, not necessarily anything about what the expected state should be. * * Signal implementations should never wake up spuriously, they are always woken up by a * signal() or signalAll(). * * This abstract definition of Signal does not need to be tied to a WaitQueue. * Whilst RegisteredSignal is the main building block of Signals, this abstract * definition allows us to compose Signals in useful ways. The Signal is 'owned' by the * thread that registered itself with WaitQueue(s) to obtain the underlying RegisteredSignal(s); * only the owning thread should use a Signal. */ public static interface Signal { /** * @return true if signalled; once true, must be discarded by the owning thread. */ public boolean isSignalled(); /** * atomically: cancels the Signal if !isSet(), or returns true if isSignalled() * * @return true if isSignalled() */ public boolean checkAndClear(); /** * Should only be called by the owning thread. Indicates the signal can be retired, * and if signalled propagates the signal to another waiting thread */ public abstract void cancel(); /** * Wait, without throwing InterruptedException, until signalled. On exit isSignalled() must be true. * If the thread is interrupted in the meantime, the interrupted flag will be set. */ public void awaitUninterruptibly(); /** * Wait until signalled, or throw an InterruptedException if interrupted before this happens. * On normal exit isSignalled() must be true; however if InterruptedException is thrown isCancelled() * will be true. * @throws InterruptedException */ public void await() throws InterruptedException; /** * Wait until signalled, or the provided time is reached, or the thread is interrupted. If signalled, * isSignalled() will be true on exit, and the method will return true; if timedout, the method will return * false and isCancelled() will be true; if interrupted an InterruptedException will be thrown and isCancelled() * will be true. * @param nanos System.nanoTime() to wait until * @return true if signalled, false if timed out * @throws InterruptedException */ public boolean awaitUntil(long nanos) throws InterruptedException; } /** * An abstract signal implementation */ public static abstract class AbstractSignal implements Signal { public void awaitUninterruptibly() { boolean interrupted = false; while (!isSignalled()) { if (Thread.interrupted()) interrupted = true; LockSupport.park(); } if (interrupted) Thread.currentThread().interrupt(); checkAndClear(); } public void await() throws InterruptedException { while (!isSignalled()) { checkInterrupted(); LockSupport.park(); } checkAndClear(); } public boolean awaitUntil(long until) throws InterruptedException { long now; while (until > (now = System.nanoTime()) && !isSignalled()) { checkInterrupted(); long delta = until - now; LockSupport.parkNanos(delta); } return checkAndClear(); } private void checkInterrupted() throws InterruptedException { if (Thread.interrupted()) { cancel(); throw new InterruptedException(); } } } /** * A signal registered with this WaitQueue */ final class RegisteredSignal extends AbstractSignal { volatile Thread thread = Thread.currentThread(); volatile int state; volatile RegisteredSignal next, prev; public boolean isSignalled() { return state == SIGNALLED; } public boolean isCancelled() { return state == CANCELLED; } public boolean isSet() { return state != NOT_SET; } private Thread signal() { if (!isSet() && signalledUpdater.compareAndSet(this, NOT_SET, SIGNALLED)) { Thread thread = this.thread; LockSupport.unpark(thread); this.thread = null; return thread; } return null; } public boolean checkAndClear() { if (!isSet() && signalledUpdater.compareAndSet(this, NOT_SET, CANCELLED)) { threadUpdater.lazySet(this, null); remove(); return false; } // must now be signalled assuming correct API usage return true; } /** * Should only be called by the registered thread. Indicates the signal can be retired, * and, if already signalled, propagates the signal to another waiting thread */ public void cancel() { if (isCancelled()) return; if (!signalledUpdater.compareAndSet(this, NOT_SET, CANCELLED)) { // must already be signalled - switch to cancelled and signalledUpdater.lazySet(this, CANCELLED); // propagate the signal WaitQueue.this.signal(); } threadUpdater.lazySet(this, null); remove(); } // attempt to edit ourselves out of the list void remove() { // in case the list has some phantom elements, we try to remove ourselves // and any contiguous adjacent range of deleted nodes // start by finding our live predecessor RegisteredSignal p = prev; while (true) { if (p == null) { // we have no live predecessor; hasWaiters() will remove us hasWaiters(); return; } if (!p.isSet()) break; p = p.prev; } // we walk forwards from our live predecessor to find the next live node, // since the forward chaining is our source of truth (prev is only a helping hand) RegisteredSignal n = p.next; // if we are the tail of the list, we cannot be removed if (n == null) return; RegisteredSignal n2; while (n.isSet() && (n2 = n.next) != null) n = n2; p.next = n; } } // for testing boolean present(Iterator<Signal> signals) { RegisteredSignal n = head; while (signals.hasNext() && n != null) { Signal s = signals.next(); while (n != null && n != s) n = n.next; } return n != null; } private static final AtomicIntegerFieldUpdater<RegisteredSignal> signalledUpdater = AtomicIntegerFieldUpdater.newUpdater(RegisteredSignal.class, "state"); private static final AtomicReferenceFieldUpdater<RegisteredSignal, RegisteredSignal> nextUpdater = AtomicReferenceFieldUpdater.newUpdater(RegisteredSignal.class, RegisteredSignal.class, "next"); private static final AtomicReferenceFieldUpdater<RegisteredSignal, RegisteredSignal> prevUpdater = AtomicReferenceFieldUpdater.newUpdater(RegisteredSignal.class, RegisteredSignal.class, "prev"); private static final AtomicReferenceFieldUpdater<RegisteredSignal, Thread> threadUpdater = AtomicReferenceFieldUpdater.newUpdater(RegisteredSignal.class, Thread.class, "thread"); }