diff options
author | Jesse Wilson <jessewilson@google.com> | 2009-07-28 17:34:57 -0700 |
---|---|---|
committer | Jesse Wilson <jessewilson@google.com> | 2009-07-28 17:35:12 -0700 |
commit | bba8d1acd6dfff06c94d761c67a30154ca5ca5df (patch) | |
tree | f68043d73ad474a98471ed8c4081b3ffa274c8f6 | |
parent | b5d658bb3d92e590f2320058064b807c4034860c (diff) | |
download | libcore-bba8d1acd6dfff06c94d761c67a30154ca5ca5df.zip libcore-bba8d1acd6dfff06c94d761c67a30154ca5ca5df.tar.gz libcore-bba8d1acd6dfff06c94d761c67a30154ca5ca5df.tar.bz2 |
Update concurrent module to Harmony r798021.
commit 56bcdc2a3b881883409267c3bd16294d80716859
Merge: 903691a f429dca
Author: Jesse Wilson <jessewilson@google.com>
Date: Mon Jul 27 17:33:19 2009 -0700
Merge branch 'concurrent_798021' into concurrent_dalvik
Conflicts:
libcore/concurrent/.classpath
libcore/concurrent/build.xml
libcore/concurrent/make/run-test.xml
libcore/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java
libcore/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java
libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java
libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentMap.java
libcore/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java
libcore/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java
libcore/concurrent/src/main/java/java/util/concurrent/DelayQueue.java
libcore/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java
libcore/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
libcore/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
libcore/concurrent/src/main/java/java/util/concurrent/Semaphore.java
libcore/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java
libcore/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
libcore/concurrent/src/main/java/java/util/concurrent/TimeUnit.java
libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java
libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java
libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java
libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java
libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java
libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java
libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java
libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
libcore/concurrent/src/main/java/java/util/concurrent/atomic/package.html
libcore/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
libcore/concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java
libcore/concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java
libcore/concurrent/src/main/java/java/util/concurrent/locks/package.html
libcore/concurrent/src/main/java/java/util/concurrent/package.html
libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongFieldUpdaterTest.java
libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceFieldUpdaterTest.java
libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceTest.java
libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ConcurrentHashMapTest.java
libcore/concurrent/src/test/java/tests/api/java/util/concurrent/DelayQueueTest.java
libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorsTest.java
libcore/concurrent/src/test/java/tests/api/java/util/concurrent/JSR166TestCase.java
libcore/concurrent/src/test/java/tests/api/java/util/concurrent/LinkedBlockingQueueTest.java
libcore/concurrent/src/test/java/tests/api/java/util/concurrent/LockSupportTest.java
libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantLockTest.java
libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantReadWriteLockTest.java
commit 903691ae71cff640d5487a3d34a20e8767dbfb66
Author: Jesse Wilson <jessewilson@google.com>
Date: Mon Jul 27 16:09:58 2009 -0700
Dalvik Concurrent
commit f429dca21c408ee62e688f60d5e110718374e944
Author: Jesse Wilson <jessewilson@google.com>
Date: Mon Jul 27 16:08:25 2009 -0700
Concurrent 798021
commit b2c76fdd1056113000140bc4af57300c87469d2d
Author: Jesse Wilson <jessewilson@google.com>
Date: Mon Jul 27 16:03:45 2009 -0700
Concurrent 527399
82 files changed, 12807 insertions, 7485 deletions
diff --git a/concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java b/concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java index ed4ce4f..f7ed15c 100644 --- a/concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java +++ b/concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java @@ -5,11 +5,10 @@ */ package java.util.concurrent; - import java.util.*; /** - * Provides default implementation of {@link ExecutorService} + * Provides default implementations of {@link ExecutorService} * execution methods. This class implements the <tt>submit</tt>, * <tt>invokeAny</tt> and <tt>invokeAll</tt> methods using the default * {@link FutureTask} class provided in this package. For example, @@ -24,6 +23,10 @@ import java.util.*; */ public abstract class AbstractExecutorService implements ExecutorService { + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); FutureTask<Object> ftask = new FutureTask<Object>(task, null); @@ -31,6 +34,10 @@ public abstract class AbstractExecutorService implements ExecutorService { return ftask; } + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ public <T> Future<T> submit(Runnable task, T result) { if (task == null) throw new NullPointerException(); FutureTask<T> ftask = new FutureTask<T>(task, result); @@ -38,6 +45,10 @@ public abstract class AbstractExecutorService implements ExecutorService { return ftask; } + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); FutureTask<T> ftask = new FutureTask<T>(task); @@ -57,7 +68,7 @@ public abstract class AbstractExecutorService implements ExecutorService { if (ntasks == 0) throw new IllegalArgumentException(); List<Future<T>> futures= new ArrayList<Future<T>>(ntasks); - ExecutorCompletionService<T> ecs = + ExecutorCompletionService<T> ecs = new ExecutorCompletionService<T>(this); // For efficiency, especially in executors with limited @@ -79,14 +90,14 @@ public abstract class AbstractExecutorService implements ExecutorService { int active = 1; for (;;) { - Future<T> f = ecs.poll(); + Future<T> f = ecs.poll(); if (f == null) { if (ntasks > 0) { --ntasks; futures.add(ecs.submit(it.next())); ++active; } - else if (active == 0) + else if (active == 0) break; else if (timed) { f = ecs.poll(nanos, TimeUnit.NANOSECONDS); @@ -96,29 +107,29 @@ public abstract class AbstractExecutorService implements ExecutorService { nanos -= now - lastTime; lastTime = now; } - else + else f = ecs.take(); } if (f != null) { --active; try { return f.get(); - } catch(InterruptedException ie) { + } catch (InterruptedException ie) { throw ie; - } catch(ExecutionException eex) { + } catch (ExecutionException eex) { ee = eex; - } catch(RuntimeException rex) { + } catch (RuntimeException rex) { ee = new ExecutionException(rex); } } - } + } if (ee == null) ee = new ExecutionException(); throw ee; } finally { - for (Future<T> f : futures) + for (Future<T> f : futures) f.cancel(true); } } @@ -133,8 +144,8 @@ public abstract class AbstractExecutorService implements ExecutorService { } } - public <T> T invokeAny(Collection<Callable<T>> tasks, - long timeout, TimeUnit unit) + public <T> T invokeAny(Collection<Callable<T>> tasks, + long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return doInvokeAny(tasks, true, unit.toNanos(timeout)); } @@ -153,10 +164,10 @@ public abstract class AbstractExecutorService implements ExecutorService { } for (Future<T> f : futures) { if (!f.isDone()) { - try { - f.get(); - } catch(CancellationException ignore) { - } catch(ExecutionException ignore) { + try { + f.get(); + } catch (CancellationException ignore) { + } catch (ExecutionException ignore) { } } } @@ -164,13 +175,13 @@ public abstract class AbstractExecutorService implements ExecutorService { return futures; } finally { if (!done) - for (Future<T> f : futures) + for (Future<T> f : futures) f.cancel(true); } } - public <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks, - long timeout, TimeUnit unit) + public <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks, + long timeout, TimeUnit unit) throws InterruptedException { if (tasks == null || unit == null) throw new NullPointerException(); @@ -178,7 +189,7 @@ public abstract class AbstractExecutorService implements ExecutorService { List<Future<T>> futures = new ArrayList<Future<T>>(tasks.size()); boolean done = false; try { - for (Callable<T> t : tasks) + for (Callable<T> t : tasks) futures.add(new FutureTask<T>(t)); long lastTime = System.nanoTime(); @@ -192,18 +203,18 @@ public abstract class AbstractExecutorService implements ExecutorService { nanos -= now - lastTime; lastTime = now; if (nanos <= 0) - return futures; + return futures; } for (Future<T> f : futures) { if (!f.isDone()) { - if (nanos <= 0) - return futures; - try { - f.get(nanos, TimeUnit.NANOSECONDS); - } catch(CancellationException ignore) { - } catch(ExecutionException ignore) { - } catch(TimeoutException toe) { + if (nanos <= 0) + return futures; + try { + f.get(nanos, TimeUnit.NANOSECONDS); + } catch (CancellationException ignore) { + } catch (ExecutionException ignore) { + } catch (TimeoutException toe) { return futures; } long now = System.nanoTime(); @@ -215,7 +226,7 @@ public abstract class AbstractExecutorService implements ExecutorService { return futures; } finally { if (!done) - for (Future<T> f : futures) + for (Future<T> f : futures) f.cancel(true); } } diff --git a/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java b/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java index 43b9fd0..0082f07 100644 --- a/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java +++ b/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java @@ -24,8 +24,8 @@ import java.util.*; * <p>This is a classic "bounded buffer", in which a * fixed-sized array holds elements inserted by producers and * extracted by consumers. Once created, the capacity cannot be - * increased. Attempts to offer an element to a full queue will - * result in the offer operation blocking; attempts to retrieve an + * increased. Attempts to <tt>put</tt> an element into a full queue + * will result in the operation blocking; attempts to <tt>take</tt> an * element from an empty queue will similarly block. * * <p> This class supports an optional fairness policy for ordering @@ -35,8 +35,9 @@ import java.util.*; * generally decreases throughput but reduces variability and avoids * starvation. * - * <p>This class implements all of the <em>optional</em> methods - * of the {@link Collection} and {@link Iterator} interfaces. + * <p>This class and its iterator implement all of the + * <em>optional</em> methods of the {@link Collection} and {@link + * Iterator} interfaces. * * @since 1.5 * @author Doug Lea @@ -56,9 +57,9 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> /** The queued items */ private final E[] items; /** items index for next take, poll or remove */ - private transient int takeIndex; + private int takeIndex; /** items index for next put, offer, or add. */ - private transient int putIndex; + private int putIndex; /** Number of items in the queue */ private int count; @@ -84,7 +85,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } /** - * Insert element at current put position, advance, and signal. + * Inserts element at current put position, advances, and signals. * Call only when holding lock. */ private void insert(E x) { @@ -95,7 +96,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } /** - * Extract element at current take position, advance, and signal. + * Extracts element at current take position, advances, and signals. * Call only when holding lock. */ private E extract() { @@ -139,6 +140,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> /** * Creates an <tt>ArrayBlockingQueue</tt> with the given (fixed) * capacity and default access policy. + * * @param capacity the capacity of this queue * @throws IllegalArgumentException if <tt>capacity</tt> is less than 1 */ @@ -149,10 +151,11 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> /** * Creates an <tt>ArrayBlockingQueue</tt> with the given (fixed) * capacity and the specified access policy. + * * @param capacity the capacity of this queue * @param fair if <tt>true</tt> then queue accesses for threads blocked - * on insertion or removal, are processed in FIFO order; if <tt>false</tt> - * the access order is unspecified. + * on insertion or removal, are processed in FIFO order; + * if <tt>false</tt> the access order is unspecified. * @throws IllegalArgumentException if <tt>capacity</tt> is less than 1 */ public ArrayBlockingQueue(int capacity, boolean fair) { @@ -169,15 +172,16 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> * capacity, the specified access policy and initially containing the * elements of the given collection, * added in traversal order of the collection's iterator. + * * @param capacity the capacity of this queue * @param fair if <tt>true</tt> then queue accesses for threads blocked - * on insertion or removal, are processed in FIFO order; if <tt>false</tt> - * the access order is unspecified. + * on insertion or removal, are processed in FIFO order; + * if <tt>false</tt> the access order is unspecified. * @param c the collection of elements to initially contain * @throws IllegalArgumentException if <tt>capacity</tt> is less than - * <tt>c.size()</tt>, or less than 1. - * @throws NullPointerException if <tt>c</tt> or any element within it - * is <tt>null</tt> + * <tt>c.size()</tt>, or less than 1. + * @throws NullPointerException if the specified collection or any + * of its elements are null */ public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c) { @@ -190,23 +194,38 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } /** - * Inserts the specified element at the tail of this queue if possible, - * returning immediately if this queue is full. + * Inserts the specified element at the tail of this queue if it is + * possible to do so immediately without exceeding the queue's capacity, + * returning <tt>true</tt> upon success and throwing an + * <tt>IllegalStateException</tt> if this queue is full. + * + * @param e the element to add + * @return <tt>true</tt> (as specified by {@link Collection#add}) + * @throws IllegalStateException if this queue is full + * @throws NullPointerException if the specified element is null + */ + public boolean add(E e) { + return super.add(e); + } + + /** + * Inserts the specified element at the tail of this queue if it is + * possible to do so immediately without exceeding the queue's capacity, + * returning <tt>true</tt> upon success and <tt>false</tt> if this queue + * is full. This method is generally preferable to method {@link #add}, + * which can fail to insert an element only by throwing an exception. * - * @param o the element to add. - * @return <tt>true</tt> if it was possible to add the element to - * this queue, else <tt>false</tt> - * @throws NullPointerException if the specified element is <tt>null</tt> + * @throws NullPointerException if the specified element is null */ - public boolean offer(E o) { - if (o == null) throw new NullPointerException(); + public boolean offer(E e) { + if (e == null) throw new NullPointerException(); final ReentrantLock lock = this.lock; lock.lock(); try { if (count == items.length) return false; else { - insert(o); + insert(e); return true; } } finally { @@ -215,29 +234,50 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } /** - * Inserts the specified element at the tail of this queue, waiting if - * necessary up to the specified wait time for space to become available. - * @param o the element to add - * @param timeout how long to wait before giving up, in units of - * <tt>unit</tt> - * @param unit a <tt>TimeUnit</tt> determining how to interpret the - * <tt>timeout</tt> parameter - * @return <tt>true</tt> if successful, or <tt>false</tt> if - * the specified waiting time elapses before space is available. - * @throws InterruptedException if interrupted while waiting. - * @throws NullPointerException if the specified element is <tt>null</tt>. + * Inserts the specified element at the tail of this queue, waiting + * for space to become available if the queue is full. + * + * @throws InterruptedException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ + public void put(E e) throws InterruptedException { + if (e == null) throw new NullPointerException(); + final E[] items = this.items; + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + try { + try { + while (count == items.length) + notFull.await(); + } catch (InterruptedException ie) { + notFull.signal(); // propagate to non-interrupted thread + throw ie; + } + insert(e); + } finally { + lock.unlock(); + } + } + + /** + * Inserts the specified element at the tail of this queue, waiting + * up to the specified wait time for space to become available if + * the queue is full. + * + * @throws InterruptedException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} */ - public boolean offer(E o, long timeout, TimeUnit unit) + public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { - if (o == null) throw new NullPointerException(); + if (e == null) throw new NullPointerException(); + long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { - long nanos = unit.toNanos(timeout); for (;;) { if (count != items.length) { - insert(o); + insert(e); return true; } if (nanos <= 0) @@ -254,7 +294,6 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } } - public E poll() { final ReentrantLock lock = this.lock; lock.lock(); @@ -268,11 +307,29 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } } + public E take() throws InterruptedException { + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + try { + try { + while (count == 0) + notEmpty.await(); + } catch (InterruptedException ie) { + notEmpty.signal(); // propagate to non-interrupted thread + throw ie; + } + E x = extract(); + return x; + } finally { + lock.unlock(); + } + } + public E poll(long timeout, TimeUnit unit) throws InterruptedException { + long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { - long nanos = unit.toNanos(timeout); for (;;) { if (count != 0) { E x = extract(); @@ -293,30 +350,6 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } } - - public boolean remove(Object o) { - if (o == null) return false; - final E[] items = this.items; - final ReentrantLock lock = this.lock; - lock.lock(); - try { - int i = takeIndex; - int k = 0; - for (;;) { - if (k++ >= count) - return false; - if (o.equals(items[i])) { - removeAt(i); - return true; - } - i = inc(i); - } - - } finally { - lock.unlock(); - } - } - public E peek() { final ReentrantLock lock = this.lock; lock.lock(); @@ -327,56 +360,12 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } } - public E take() throws InterruptedException { - final ReentrantLock lock = this.lock; - lock.lockInterruptibly(); - try { - try { - while (count == 0) - notEmpty.await(); - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to non-interrupted thread - throw ie; - } - E x = extract(); - return x; - } finally { - lock.unlock(); - } - } - - /** - * Adds the specified element to the tail of this queue, waiting if - * necessary for space to become available. - * @param o the element to add - * @throws InterruptedException if interrupted while waiting. - * @throws NullPointerException if the specified element is <tt>null</tt>. - */ - public void put(E o) throws InterruptedException { - if (o == null) throw new NullPointerException(); - final E[] items = this.items; - final ReentrantLock lock = this.lock; - lock.lockInterruptibly(); - try { - try { - while (count == items.length) - notFull.await(); - } catch (InterruptedException ie) { - notFull.signal(); // propagate to non-interrupted thread - throw ie; - } - insert(o); - } finally { - lock.unlock(); - } - } - // this doc comment is overridden to remove the reference to collections // greater in size than Integer.MAX_VALUE /** * Returns the number of elements in this queue. * - * @return the number of elements in this queue. + * @return the number of elements in this queue */ public int size() { final ReentrantLock lock = this.lock; @@ -391,17 +380,15 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> // this doc comment is a modified copy of the inherited doc comment, // without the reference to unlimited queues. /** - * Returns the number of elements that this queue can ideally (in - * the absence of memory or resource constraints) accept without + * Returns the number of additional elements that this queue can ideally + * (in the absence of memory or resource constraints) accept without * blocking. This is always equal to the initial capacity of this queue * less the current <tt>size</tt> of this queue. - * <p>Note that you <em>cannot</em> always tell if - * an attempt to <tt>add</tt> an element will succeed by - * inspecting <tt>remainingCapacity</tt> because it may be the - * case that a waiting consumer is ready to <tt>take</tt> an - * element out of an otherwise full queue. - * - * @return the remaining capacity + * + * <p>Note that you <em>cannot</em> always tell if an attempt to insert + * an element will succeed by inspecting <tt>remainingCapacity</tt> + * because it may be the case that another thread is about to + * insert or remove an element. */ public int remainingCapacity() { final ReentrantLock lock = this.lock; @@ -413,7 +400,48 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } } + /** + * Removes a single instance of the specified element from this queue, + * if it is present. More formally, removes an element <tt>e</tt> such + * that <tt>o.equals(e)</tt>, if this queue contains one or more such + * elements. + * Returns <tt>true</tt> if this queue contained the specified element + * (or equivalently, if this queue changed as a result of the call). + * + * @param o element to be removed from this queue, if present + * @return <tt>true</tt> if this queue changed as a result of the call + */ + public boolean remove(Object o) { + if (o == null) return false; + final E[] items = this.items; + final ReentrantLock lock = this.lock; + lock.lock(); + try { + int i = takeIndex; + int k = 0; + for (;;) { + if (k++ >= count) + return false; + if (o.equals(items[i])) { + removeAt(i); + return true; + } + i = inc(i); + } + + } finally { + lock.unlock(); + } + } + /** + * Returns <tt>true</tt> if this queue contains the specified element. + * More formally, returns <tt>true</tt> if and only if this queue contains + * at least one element <tt>e</tt> such that <tt>o.equals(e)</tt>. + * + * @param o object to be checked for containment in this queue + * @return <tt>true</tt> if this queue contains the specified element + */ public boolean contains(Object o) { if (o == null) return false; final E[] items = this.items; @@ -433,6 +461,19 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } } + /** + * Returns an array containing all of the elements in this queue, in + * proper sequence. + * + * <p>The returned array will be "safe" in that no references to it are + * maintained by this queue. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + * <p>This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this queue + */ public Object[] toArray() { final E[] items = this.items; final ReentrantLock lock = this.lock; @@ -451,6 +492,42 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } } + /** + * Returns an array containing all of the elements in this queue, in + * proper sequence; the runtime type of the returned array is that of + * the specified array. If the queue fits in the specified array, it + * is returned therein. Otherwise, a new array is allocated with the + * runtime type of the specified array and the size of this queue. + * + * <p>If this queue fits in the specified array with room to spare + * (i.e., the array has more elements than this queue), the element in + * the array immediately following the end of the queue is set to + * <tt>null</tt>. + * + * <p>Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + * <p>Suppose <tt>x</tt> is a queue known to contain only strings. + * The following code can be used to dump the queue into a newly + * allocated array of <tt>String</tt>: + * + * <pre> + * String[] y = x.toArray(new String[0]);</pre> + * + * Note that <tt>toArray(new Object[0])</tt> is identical in function to + * <tt>toArray()</tt>. + * + * @param a the array into which the elements of the queue are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose + * @return an array containing all of the elements in this queue + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this queue + * @throws NullPointerException if the specified array is null + */ public <T> T[] toArray(T[] a) { final E[] items = this.items; final ReentrantLock lock = this.lock; @@ -486,7 +563,10 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } } - + /** + * Atomically removes all of the elements from this queue. + * The queue will be empty after this call returns. + */ public void clear() { final E[] items = this.items; final ReentrantLock lock = this.lock; @@ -507,6 +587,12 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } } + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection<? super E> c) { if (c == null) throw new NullPointerException(); @@ -537,7 +623,12 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } } - + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection<? super E> c, int maxElements) { if (c == null) throw new NullPointerException(); @@ -574,12 +665,12 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> /** * Returns an iterator over the elements in this queue in proper sequence. * The returned <tt>Iterator</tt> is a "weakly consistent" iterator that - * will never throw {@link java.util.ConcurrentModificationException}, + * will never throw {@link ConcurrentModificationException}, * and guarantees to traverse elements as they existed upon * construction of the iterator, and may (but is not guaranteed to) * reflect any modifications subsequent to construction. * - * @return an iterator over the elements in this queue in proper sequence. + * @return an iterator over the elements in this queue in proper sequence */ public Iterator<E> iterator() { final ReentrantLock lock = this.lock; @@ -606,7 +697,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> * that an element exists in hasNext(), we must return it in * the following next() call even if it was in the process of * being removed when hasNext() was called. - **/ + */ private E nextItem; /** @@ -635,7 +726,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } /** - * Check whether nextIndex is valid; if so setting nextItem. + * Checks whether nextIndex is valid; if so setting nextItem. * Stops iterator when either hits putIndex or sees null item. */ private void checkNext() { diff --git a/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java b/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java index ddb7d77..85945b9 100644 --- a/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java +++ b/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java @@ -15,9 +15,50 @@ import java.util.Queue; /** * A {@link java.util.Queue} that additionally supports operations - * that wait for the queue to become non-empty when retrieving an element, - * and wait for space to become available in the queue when storing an - * element. + * that wait for the queue to become non-empty when retrieving an + * element, and wait for space to become available in the queue when + * storing an element. + * + * <p><tt>BlockingQueue</tt> methods come in four forms, with different ways + * of handling operations that cannot be satisfied immediately, but may be + * satisfied at some point in the future: + * one throws an exception, the second returns a special value (either + * <tt>null</tt> or <tt>false</tt>, depending on the operation), the third + * blocks the current thread indefinitely until the operation can succeed, + * and the fourth blocks for only a given maximum time limit before giving + * up. These methods are summarized in the following table: + * + * <p> + * <table BORDER CELLPADDING=3 CELLSPACING=1> + * <tr> + * <td></td> + * <td ALIGN=CENTER><em>Throws exception</em></td> + * <td ALIGN=CENTER><em>Special value</em></td> + * <td ALIGN=CENTER><em>Blocks</em></td> + * <td ALIGN=CENTER><em>Times out</em></td> + * </tr> + * <tr> + * <td><b>Insert</b></td> + * <td>{@link #add add(e)}</td> + * <td>{@link #offer offer(e)}</td> + * <td>{@link #put put(e)}</td> + * <td>{@link #offer(Object, long, TimeUnit) offer(e, time, unit)}</td> + * </tr> + * <tr> + * <td><b>Remove</b></td> + * <td>{@link #remove remove()}</td> + * <td>{@link #poll poll()}</td> + * <td>{@link #take take()}</td> + * <td>{@link #poll(long, TimeUnit) poll(time, unit)}</td> + * </tr> + * <tr> + * <td><b>Examine</b></td> + * <td>{@link #element element()}</td> + * <td>{@link #peek peek()}</td> + * <td><em>not applicable</em></td> + * <td><em>not applicable</em></td> + * </tr> + * </table> * * <p>A <tt>BlockingQueue</tt> does not accept <tt>null</tt> elements. * Implementations throw <tt>NullPointerException</tt> on attempts @@ -31,16 +72,22 @@ import java.util.Queue; * A <tt>BlockingQueue</tt> without any intrinsic capacity constraints always * reports a remaining capacity of <tt>Integer.MAX_VALUE</tt>. * - * <p> While <tt>BlockingQueue</tt> is designed to be used primarily - * for producer-consumer queues, it additionally supports the {@link - * java.util.Collection} interface. So, for example, it is possible - * to remove an arbitrary element from a queue using + * <p> <tt>BlockingQueue</tt> implementations are designed to be used + * primarily for producer-consumer queues, but additionally support + * the {@link java.util.Collection} interface. So, for example, it is + * possible to remove an arbitrary element from a queue using * <tt>remove(x)</tt>. However, such operations are in general * <em>not</em> performed very efficiently, and are intended for only - * occasional use, such as when a queued message is cancelled. Also, - * the bulk Collection operations, most notably <tt>addAll</tt>, are - * <em>not</em> necessarily performed atomically, so it is possible - * for <tt>addAll(c)</tt> to fail (throwing an exception) after adding + * occasional use, such as when a queued message is cancelled. + * + * <p> <tt>BlockingQueue</tt> implementations are thread-safe. All + * queuing methods achieve their effects atomically using internal + * locks or other forms of concurrency control. However, the + * <em>bulk</em> Collection operations <tt>addAll</tt>, + * <tt>containsAll</tt>, <tt>retainAll</tt> and <tt>removeAll</tt> are + * <em>not</em> necessarily performed atomically unless specified + * otherwise in an implementation. So it is possible, for example, for + * <tt>addAll(c)</tt> to fail (throwing an exception) after adding * only some of the elements in <tt>c</tt>. * * <p>A <tt>BlockingQueue</tt> does <em>not</em> intrinsically support @@ -61,7 +108,7 @@ import java.util.Queue; * Producer(BlockingQueue q) { queue = q; } * public void run() { * try { - * while(true) { queue.put(produce()); } + * while (true) { queue.put(produce()); } * } catch (InterruptedException ex) { ... handle ...} * } * Object produce() { ... } @@ -72,7 +119,7 @@ import java.util.Queue; * Consumer(BlockingQueue q) { queue = q; } * public void run() { * try { - * while(true) { consume(queue.take()); } + * while (true) { consume(queue.take()); } * } catch (InterruptedException ex) { ... handle ...} * } * void consume(Object x) { ... } @@ -91,137 +138,207 @@ import java.util.Queue; * } * </pre> * + * <p>Memory consistency effects: As with other concurrent + * collections, actions in a thread prior to placing an object into a + * {@code BlockingQueue} + * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> + * actions subsequent to the access or removal of that element from + * the {@code BlockingQueue} in another thread. + * * @since 1.5 * @author Doug Lea * @param <E> the type of elements held in this collection */ public interface BlockingQueue<E> extends Queue<E> { + /** + * Inserts the specified element into this queue if it is possible to do + * so immediately without violating capacity restrictions, returning + * <tt>true</tt> upon success and throwing an + * <tt>IllegalStateException</tt> if no space is currently available. + * When using a capacity-restricted queue, it is generally preferable to + * use {@link #offer(Object) offer}. + * + * @param e the element to add + * @return <tt>true</tt> (as specified by {@link Collection#add}) + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this queue + */ + boolean add(E e); /** - * Inserts the specified element into this queue, if possible. When - * using queues that may impose insertion restrictions (for - * example capacity bounds), method <tt>offer</tt> is generally - * preferable to method {@link Collection#add}, which can fail to - * insert an element only by throwing an exception. + * Inserts the specified element into this queue if it is possible to do + * so immediately without violating capacity restrictions, returning + * <tt>true</tt> upon success and <tt>false</tt> if no space is currently + * available. When using a capacity-restricted queue, this method is + * generally preferable to {@link #add}, which can fail to insert an + * element only by throwing an exception. * - * @param o the element to add. - * @return <tt>true</tt> if it was possible to add the element to - * this queue, else <tt>false</tt> - * @throws NullPointerException if the specified element is <tt>null</tt> + * @param e the element to add + * @return <tt>true</tt> if the element was added to this queue, else + * <tt>false</tt> + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this queue */ - boolean offer(E o); - + boolean offer(E e); + /** * Inserts the specified element into this queue, waiting if necessary - * up to the specified wait time for space to become available. - * @param o the element to add - * @param timeout how long to wait before giving up, in units of - * <tt>unit</tt> - * @param unit a <tt>TimeUnit</tt> determining how to interpret the - * <tt>timeout</tt> parameter - * @return <tt>true</tt> if successful, or <tt>false</tt> if - * the specified waiting time elapses before space is available. - * @throws InterruptedException if interrupted while waiting. - * @throws NullPointerException if the specified element is <tt>null</tt>. + * for space to become available. + * + * @param e the element to add + * @throws InterruptedException if interrupted while waiting + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this queue */ - boolean offer(E o, long timeout, TimeUnit unit) - throws InterruptedException; + void put(E e) throws InterruptedException; /** - * Retrieves and removes the head of this queue, waiting - * if necessary up to the specified wait time if no elements are - * present on this queue. + * Inserts the specified element into this queue, waiting up to the + * specified wait time if necessary for space to become available. + * + * @param e the element to add * @param timeout how long to wait before giving up, in units of - * <tt>unit</tt> + * <tt>unit</tt> * @param unit a <tt>TimeUnit</tt> determining how to interpret the - * <tt>timeout</tt> parameter - * @return the head of this queue, or <tt>null</tt> if the - * specified waiting time elapses before an element is present. - * @throws InterruptedException if interrupted while waiting. + * <tt>timeout</tt> parameter + * @return <tt>true</tt> if successful, or <tt>false</tt> if + * the specified waiting time elapses before space is available + * @throws InterruptedException if interrupted while waiting + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this queue */ - E poll(long timeout, TimeUnit unit) + boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException; /** - * Retrieves and removes the head of this queue, waiting - * if no elements are present on this queue. + * Retrieves and removes the head of this queue, waiting if necessary + * until an element becomes available. + * * @return the head of this queue - * @throws InterruptedException if interrupted while waiting. + * @throws InterruptedException if interrupted while waiting */ E take() throws InterruptedException; /** - * Adds the specified element to this queue, waiting if necessary for - * space to become available. - * @param o the element to add - * @throws InterruptedException if interrupted while waiting. - * @throws NullPointerException if the specified element is <tt>null</tt>. + * Retrieves and removes the head of this queue, waiting up to the + * specified wait time if necessary for an element to become available. + * + * @param timeout how long to wait before giving up, in units of + * <tt>unit</tt> + * @param unit a <tt>TimeUnit</tt> determining how to interpret the + * <tt>timeout</tt> parameter + * @return the head of this queue, or <tt>null</tt> if the + * specified waiting time elapses before an element is available + * @throws InterruptedException if interrupted while waiting */ - void put(E o) throws InterruptedException; + E poll(long timeout, TimeUnit unit) + throws InterruptedException; /** - * Returns the number of elements that this queue can ideally (in - * the absence of memory or resource constraints) accept without - * blocking, or <tt>Integer.MAX_VALUE</tt> if there is no - * intrinsic limit. - * <p>Note that you <em>cannot</em> always tell if - * an attempt to <tt>add</tt> an element will succeed by - * inspecting <tt>remainingCapacity</tt> because it may be the - * case that a waiting consumer is ready to <tt>take</tt> an - * element out of an otherwise full queue. + * Returns the number of additional elements that this queue can ideally + * (in the absence of memory or resource constraints) accept without + * blocking, or <tt>Integer.MAX_VALUE</tt> if there is no intrinsic + * limit. + * + * <p>Note that you <em>cannot</em> always tell if an attempt to insert + * an element will succeed by inspecting <tt>remainingCapacity</tt> + * because it may be the case that another thread is about to + * insert or remove an element. + * * @return the remaining capacity */ int remainingCapacity(); /** - * Adds the specified element to this queue if it is possible to - * do so immediately, returning <tt>true</tt> upon success, else - * throwing an IllegalStateException. - * @param o the element - * @return <tt>true</tt> (as per the general contract of - * <tt>Collection.add</tt>). + * Removes a single instance of the specified element from this queue, + * if it is present. More formally, removes an element <tt>e</tt> such + * that <tt>o.equals(e)</tt>, if this queue contains one or more such + * elements. + * Returns <tt>true</tt> if this queue contained the specified element + * (or equivalently, if this queue changed as a result of the call). + * + * @param o element to be removed from this queue, if present + * @return <tt>true</tt> if this queue changed as a result of the call + * @throws ClassCastException if the class of the specified element + * is incompatible with this queue (optional) + * @throws NullPointerException if the specified element is null (optional) + */ + boolean remove(Object o); + + /** + * Returns <tt>true</tt> if this queue contains the specified element. + * More formally, returns <tt>true</tt> if and only if this queue contains + * at least one element <tt>e</tt> such that <tt>o.equals(e)</tt>. * - * @throws NullPointerException if the specified element is <tt>null</tt> - * @throws IllegalStateException if element cannot be added + * @param o object to be checked for containment in this queue + * @return <tt>true</tt> if this queue contains the specified element + * @throws ClassCastException if the class of the specified element + * is incompatible with this queue (optional) + * @throws NullPointerException if the specified element is null (optional) */ - boolean add(E o); + public boolean contains(Object o); /** * Removes all available elements from this queue and adds them - * into the given collection. This operation may be more + * to the given collection. This operation may be more * efficient than repeatedly polling this queue. A failure - * encountered while attempting to <tt>add</tt> elements to + * encountered while attempting to add elements to * collection <tt>c</tt> may result in elements being in neither, * either or both collections when the associated exception is - * thrown. Attempts to drain a queue to itself result in + * thrown. Attempts to drain a queue to itself result in * <tt>IllegalArgumentException</tt>. Further, the behavior of * this operation is undefined if the specified collection is * modified while the operation is in progress. * * @param c the collection to transfer elements into - * @return the number of elements transferred. - * @throws NullPointerException if c is null - * @throws IllegalArgumentException if c is this queue - * + * @return the number of elements transferred + * @throws UnsupportedOperationException if addition of elements + * is not supported by the specified collection + * @throws ClassCastException if the class of an element of this queue + * prevents it from being added to the specified collection + * @throws NullPointerException if the specified collection is null + * @throws IllegalArgumentException if the specified collection is this + * queue, or some property of an element of this queue prevents + * it from being added to the specified collection */ int drainTo(Collection<? super E> c); - + /** * Removes at most the given number of available elements from - * this queue and adds them into the given collection. A failure - * encountered while attempting to <tt>add</tt> elements to + * this queue and adds them to the given collection. A failure + * encountered while attempting to add elements to * collection <tt>c</tt> may result in elements being in neither, * either or both collections when the associated exception is - * thrown. Attempts to drain a queue to itself result in + * thrown. Attempts to drain a queue to itself result in * <tt>IllegalArgumentException</tt>. Further, the behavior of * this operation is undefined if the specified collection is * modified while the operation is in progress. * * @param c the collection to transfer elements into * @param maxElements the maximum number of elements to transfer - * @return the number of elements transferred. - * @throws NullPointerException if c is null - * @throws IllegalArgumentException if c is this queue + * @return the number of elements transferred + * @throws UnsupportedOperationException if addition of elements + * is not supported by the specified collection + * @throws ClassCastException if the class of an element of this queue + * prevents it from being added to the specified collection + * @throws NullPointerException if the specified collection is null + * @throws IllegalArgumentException if the specified collection is this + * queue, or some property of an element of this queue prevents + * it from being added to the specified collection */ int drainTo(Collection<? super E> c, int maxElements); } diff --git a/concurrent/src/main/java/java/util/concurrent/CompletionService.java b/concurrent/src/main/java/java/util/concurrent/CompletionService.java index e349e5f..df9f719 100644 --- a/concurrent/src/main/java/java/util/concurrent/CompletionService.java +++ b/concurrent/src/main/java/java/util/concurrent/CompletionService.java @@ -16,52 +16,56 @@ package java.util.concurrent; * submitted in one part of a program or system, and then acted upon * in a different part of the program when the reads complete, * possibly in a different order than they were requested. - - * <p> * - * Typically, a <tt>CompletionService</tt> relies on a separate {@link - * Executor} to actually execute the tasks, in which case the + * <p>Typically, a <tt>CompletionService</tt> relies on a separate + * {@link Executor} to actually execute the tasks, in which case the * <tt>CompletionService</tt> only manages an internal completion * queue. The {@link ExecutorCompletionService} class provides an * implementation of this approach. * + * <p>Memory consistency effects: Actions in a thread prior to + * submitting a task to a {@code CompletionService} + * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> + * actions taken by that task, which in turn <i>happen-before</i> + * actions following a successful return from the corresponding {@code take()}. + * */ public interface CompletionService<V> { /** * Submits a value-returning task for execution and returns a Future - * representing the pending results of the task. Upon completion, + * representing the pending results of the task. Upon completion, * this task may be taken or polled. * * @param task the task to submit * @return a Future representing pending completion of the task - * @throws RejectedExecutionException if task cannot be scheduled - * for execution - * @throws NullPointerException if task null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + * @throws NullPointerException if the task is null */ Future<V> submit(Callable<V> task); - /** - * Submits a Runnable task for execution and returns a Future - * representing that task. Upon completion, - * this task may be taken or polled. + * Submits a Runnable task for execution and returns a Future + * representing that task. Upon completion, this task may be + * taken or polled. * * @param task the task to submit * @param result the result to return upon successful completion * @return a Future representing pending completion of the task, - * and whose <tt>get()</tt> method will return the given result value - * upon completion - * @throws RejectedExecutionException if task cannot be scheduled - * for execution - * @throws NullPointerException if task null + * and whose <tt>get()</tt> method will return the given + * result value upon completion + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + * @throws NullPointerException if the task is null */ Future<V> submit(Runnable task, V result); /** * Retrieves and removes the Future representing the next * completed task, waiting if none are yet present. + * * @return the Future representing the next completed task - * @throws InterruptedException if interrupted while waiting. + * @throws InterruptedException if interrupted while waiting */ Future<V> take() throws InterruptedException; @@ -71,7 +75,7 @@ public interface CompletionService<V> { * completed task or <tt>null</tt> if none are present. * * @return the Future representing the next completed task, or - * <tt>null</tt> if none are present. + * <tt>null</tt> if none are present */ Future<V> poll(); @@ -79,14 +83,15 @@ public interface CompletionService<V> { * Retrieves and removes the Future representing the next * completed task, waiting if necessary up to the specified wait * time if none are yet present. + * * @param timeout how long to wait before giving up, in units of - * <tt>unit</tt> + * <tt>unit</tt> * @param unit a <tt>TimeUnit</tt> determining how to interpret the - * <tt>timeout</tt> parameter + * <tt>timeout</tt> parameter * @return the Future representing the next completed task or - * <tt>null</tt> if the specified waiting time elapses before one - * is present. - * @throws InterruptedException if interrupted while waiting. + * <tt>null</tt> if the specified waiting time elapses + * before one is present + * @throws InterruptedException if interrupted while waiting */ Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException; } diff --git a/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java b/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java index f72c6fe..cb5fb3f 100644 --- a/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java +++ b/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java @@ -14,7 +14,6 @@ import java.io.ObjectOutputStream; // BEGIN android-note // removed link to collections framework docs -// removed cloneable interface from ConcurrentMap interface // END android-note /** @@ -38,13 +37,12 @@ import java.io.ObjectOutputStream; * removal of only some entries. Similarly, Iterators and * Enumerations return elements reflecting the state of the hash table * at some point at or since the creation of the iterator/enumeration. - * They do <em>not</em> throw - * {@link ConcurrentModificationException}. However, iterators are - * designed to be used by only one thread at a time. + * They do <em>not</em> throw {@link ConcurrentModificationException}. + * However, iterators are designed to be used by only one thread at a time. * * <p> The allowed concurrency among update operations is guided by * the optional <tt>concurrencyLevel</tt> constructor argument - * (default 16), which is used as a hint for internal sizing. The + * (default <tt>16</tt>), which is used as a hint for internal sizing. The * table is internally partitioned to try to permit the indicated * number of concurrent updates without contention. Because placement * in hash tables is essentially random, the actual concurrency will @@ -54,20 +52,23 @@ import java.io.ObjectOutputStream; * and a significantly lower value can lead to thread contention. But * overestimates and underestimates within an order of magnitude do * not usually have much noticeable impact. A value of one is - * appropriate when it is known that only one thread will modify - * and all others will only read. + * appropriate when it is known that only one thread will modify and + * all others will only read. Also, resizing this or any other kind of + * hash table is a relatively slow operation, so, when possible, it is + * a good idea to provide estimates of expected table sizes in + * constructors. * - * <p>This class implements all of the <em>optional</em> methods - * of the {@link Map} and {@link Iterator} interfaces. + * <p>This class and its views and iterators implement all of the + * <em>optional</em> methods of the {@link Map} and {@link Iterator} + * interfaces. * - * <p> Like {@link java.util.Hashtable} but unlike {@link - * java.util.HashMap}, this class does NOT allow <tt>null</tt> to be - * used as a key or value. + * <p> Like {@link Hashtable} but unlike {@link HashMap}, this class + * does <em>not</em> allow <tt>null</tt> to be used as a key or value. * * @since 1.5 * @author Doug Lea * @param <K> the type of keys maintained by this map - * @param <V> the type of mapped values + * @param <V> the type of mapped values */ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>, Serializable { @@ -81,29 +82,30 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> /* ---------------- Constants -------------- */ /** - * The default initial number of table slots for this table. - * Used when not otherwise specified in constructor. + * The default initial capacity for this table, + * used when not otherwise specified in a constructor. */ - static int DEFAULT_INITIAL_CAPACITY = 16; + static final int DEFAULT_INITIAL_CAPACITY = 16; /** - * The maximum capacity, used if a higher value is implicitly - * specified by either of the constructors with arguments. MUST - * be a power of two <= 1<<30 to ensure that entries are indexible - * using ints. + * The default load factor for this table, used when not + * otherwise specified in a constructor. */ - static final int MAXIMUM_CAPACITY = 1 << 30; + static final float DEFAULT_LOAD_FACTOR = 0.75f; /** - * The default load factor for this table. Used when not - * otherwise specified in constructor. + * The default concurrency level for this table, used when not + * otherwise specified in a constructor. */ - static final float DEFAULT_LOAD_FACTOR = 0.75f; + static final int DEFAULT_CONCURRENCY_LEVEL = 16; /** - * The default number of concurrency control segments. - **/ - static final int DEFAULT_SEGMENTS = 16; + * The maximum capacity, used if a higher value is implicitly + * specified by either of the constructors with arguments. MUST + * be a power of two <= 1<<30 to ensure that entries are indexable + * using ints. + */ + static final int MAXIMUM_CAPACITY = 1 << 30; /** * The maximum number of segments to allow; used to bound @@ -111,23 +113,31 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> */ static final int MAX_SEGMENTS = 1 << 16; // slightly conservative + /** + * Number of unsynchronized retries in size and containsValue + * methods before resorting to locking. This is used to avoid + * unbounded retries if tables undergo continuous modification + * which would make it impossible to obtain an accurate result. + */ + static final int RETRIES_BEFORE_LOCK = 2; + /* ---------------- Fields -------------- */ /** * Mask value for indexing into segments. The upper bits of a * key's hash code are used to choose the segment. - **/ + */ final int segmentMask; /** * Shift value for indexing within segments. - **/ + */ final int segmentShift; /** * The segments, each of which is a specialized hash table */ - final Segment[] segments; + final Segment<K,V>[] segments; transient Set<K> keySet; transient Set<Map.Entry<K,V>> entrySet; @@ -136,18 +146,21 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> /* ---------------- Small Utilities -------------- */ /** - * Returns a hash code for non-null Object x. - * Uses the same hash code spreader as most other java.util hash tables. - * @param x the object serving as a key - * @return the hash code + * Applies a supplemental hash function to a given hashCode, which + * defends against poor quality hash functions. This is critical + * because ConcurrentHashMap uses power-of-two length hash tables, + * that otherwise encounter collisions for hashCodes that do not + * differ in lower or upper bits. */ - static int hash(Object x) { - int h = x.hashCode(); - h += ~(h << 9); - h ^= (h >>> 14); - h += (h << 4); - h ^= (h >>> 10); - return h; + private static int hash(int h) { + // Spread bits to regularize both segment and index locations, + // using variant of single-word Wang/Jenkins hash. + h += (h << 15) ^ 0xffffcd7d; + h ^= (h >>> 10); + h += (h << 3); + h ^= (h >>> 6); + h += (h << 2) + (h << 14); + return h ^ (h >>> 16); } /** @@ -156,16 +169,47 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * @return the segment */ final Segment<K,V> segmentFor(int hash) { - return (Segment<K,V>) segments[(hash >>> segmentShift) & segmentMask]; + return segments[(hash >>> segmentShift) & segmentMask]; } /* ---------------- Inner Classes -------------- */ /** + * ConcurrentHashMap list entry. Note that this is never exported + * out as a user-visible Map.Entry. + * + * Because the value field is volatile, not final, it is legal wrt + * the Java Memory Model for an unsynchronized reader to see null + * instead of initial value when read via a data race. Although a + * reordering leading to this is not likely to ever actually + * occur, the Segment.readValueUnderLock method is used as a + * backup in case a null (pre-initialized) value is ever seen in + * an unsynchronized access method. + */ + static final class HashEntry<K,V> { + final K key; + final int hash; + volatile V value; + final HashEntry<K,V> next; + + HashEntry(K key, int hash, HashEntry<K,V> next, V value) { + this.key = key; + this.hash = hash; + this.next = next; + this.value = value; + } + + @SuppressWarnings("unchecked") + static final <K,V> HashEntry<K,V>[] newArray(int i) { + return new HashEntry[i]; + } + } + + /** * Segments are specialized versions of hash tables. This * subclasses from ReentrantLock opportunistically, just to * simplify some locking and avoid separate construction. - **/ + */ static final class Segment<K,V> extends ReentrantLock implements Serializable { /* * Segments maintain a table of entry lists that are ALWAYS @@ -179,58 +223,59 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * is less than two for the default load factor threshold.) * * Read operations can thus proceed without locking, but rely - * on a memory barrier to ensure that completed write - * operations performed by other threads are - * noticed. Conveniently, the "count" field, tracking the - * number of elements, can also serve as the volatile variable - * providing proper read/write barriers. This is convenient - * because this field needs to be read in many read operations - * anyway. - * - * Implementors note. The basic rules for all this are: + * on selected uses of volatiles to ensure that completed + * write operations performed by other threads are + * noticed. For most purposes, the "count" field, tracking the + * number of elements, serves as that volatile variable + * ensuring visibility. This is convenient because this field + * needs to be read in many read operations anyway: * - * - All unsynchronized read operations must first read the + * - All (unsynchronized) read operations must first read the * "count" field, and should not look at table entries if * it is 0. * - * - All synchronized write operations should write to - * the "count" field after updating. The operations must not - * take any action that could even momentarily cause - * a concurrent read operation to see inconsistent - * data. This is made easier by the nature of the read - * operations in Map. For example, no operation + * - All (synchronized) write operations should write to + * the "count" field after structurally changing any bin. + * The operations must not take any action that could even + * momentarily cause a concurrent read operation to see + * inconsistent data. This is made easier by the nature of + * the read operations in Map. For example, no operation * can reveal that the table has grown but the threshold * has not yet been updated, so there are no atomicity * requirements for this with respect to reads. * - * As a guide, all critical volatile reads and writes are marked - * in code comments. + * As a guide, all critical volatile reads and writes to the + * count field are marked in code comments. */ private static final long serialVersionUID = 2249069246763182397L; /** * The number of elements in this segment's region. - **/ + */ transient volatile int count; /** - * Number of updates; used for checking lack of modifications - * in bulk-read methods. + * Number of updates that alter the size of the table. This is + * used during bulk-read methods to make sure they see a + * consistent snapshot: If modCounts change during a traversal + * of segments computing size or checking containsValue, then + * we might have an inconsistent view of state so (usually) + * must retry. */ transient int modCount; /** * The table is rehashed when its size exceeds this threshold. - * (The value of this field is always (int)(capacity * - * loadFactor).) + * (The value of this field is always <tt>(int)(capacity * + * loadFactor)</tt>.) */ transient int threshold; /** - * The per-segment table + * The per-segment table. */ - transient HashEntry[] table; + transient volatile HashEntry<K,V>[] table; /** * The load factor for the hash table. Even though this value @@ -242,29 +287,59 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> Segment(int initialCapacity, float lf) { loadFactor = lf; - setTable(new HashEntry[initialCapacity]); + setTable(HashEntry.<K,V>newArray(initialCapacity)); + } + + @SuppressWarnings("unchecked") + static final <K,V> Segment<K,V>[] newArray(int i) { + return new Segment[i]; } /** - * Set table to new HashEntry array. + * Sets table to new HashEntry array. * Call only while holding lock or in constructor. - **/ - void setTable(HashEntry[] newTable) { - table = newTable; + */ + void setTable(HashEntry<K,V>[] newTable) { threshold = (int)(newTable.length * loadFactor); - count = count; // write-volatile + table = newTable; + } + + /** + * Returns properly casted first entry of bin for given hash. + */ + HashEntry<K,V> getFirst(int hash) { + HashEntry<K,V>[] tab = table; + return tab[hash & (tab.length - 1)]; + } + + /** + * Reads value field of an entry under lock. Called if value + * field ever appears to be null. This is possible only if a + * compiler happens to reorder a HashEntry initialization with + * its table assignment, which is legal under memory model + * but is not known to ever occur. + */ + V readValueUnderLock(HashEntry<K,V> e) { + lock(); + try { + return e.value; + } finally { + unlock(); + } } /* Specialized implementations of map methods */ V get(Object key, int hash) { if (count != 0) { // read-volatile - HashEntry[] tab = table; - int index = hash & (tab.length - 1); - HashEntry<K,V> e = (HashEntry<K,V>) tab[index]; + HashEntry<K,V> e = getFirst(hash); while (e != null) { - if (e.hash == hash && key.equals(e.key)) - return e.value; + if (e.hash == hash && key.equals(e.key)) { + V v = e.value; + if (v != null) + return v; + return readValueUnderLock(e); // recheck + } e = e.next; } } @@ -273,9 +348,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> boolean containsKey(Object key, int hash) { if (count != 0) { // read-volatile - HashEntry[] tab = table; - int index = hash & (tab.length - 1); - HashEntry<K,V> e = (HashEntry<K,V>) tab[index]; + HashEntry<K,V> e = getFirst(hash); while (e != null) { if (e.hash == hash && key.equals(e.key)) return true; @@ -287,12 +360,17 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> boolean containsValue(Object value) { if (count != 0) { // read-volatile - HashEntry[] tab = table; + HashEntry<K,V>[] tab = table; int len = tab.length; - for (int i = 0 ; i < len; i++) - for (HashEntry<K,V> e = (HashEntry<K,V>)tab[i] ; e != null ; e = e.next) - if (value.equals(e.value)) + for (int i = 0 ; i < len; i++) { + for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) { + V v = e.value; + if (v == null) // recheck + v = readValueUnderLock(e); + if (value.equals(v)) return true; + } + } } return false; } @@ -300,27 +378,16 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> boolean replace(K key, int hash, V oldValue, V newValue) { lock(); try { - int c = count; - HashEntry[] tab = table; - int index = hash & (tab.length - 1); - HashEntry<K,V> first = (HashEntry<K,V>) tab[index]; - HashEntry<K,V> e = first; - for (;;) { - if (e == null) - return false; - if (e.hash == hash && key.equals(e.key)) - break; + HashEntry<K,V> e = getFirst(hash); + while (e != null && (e.hash != hash || !key.equals(e.key))) e = e.next; - } - V v = e.value; - if (v == null || !oldValue.equals(v)) - return false; - - e.value = newValue; - count = c; // write-volatile - return true; - + boolean replaced = false; + if (e != null && oldValue.equals(e.value)) { + replaced = true; + e.value = newValue; + } + return replaced; } finally { unlock(); } @@ -329,24 +396,16 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> V replace(K key, int hash, V newValue) { lock(); try { - int c = count; - HashEntry[] tab = table; - int index = hash & (tab.length - 1); - HashEntry<K,V> first = (HashEntry<K,V>) tab[index]; - HashEntry<K,V> e = first; - for (;;) { - if (e == null) - return null; - if (e.hash == hash && key.equals(e.key)) - break; + HashEntry<K,V> e = getFirst(hash); + while (e != null && (e.hash != hash || !key.equals(e.key))) e = e.next; - } - V v = e.value; - e.value = newValue; - count = c; // write-volatile - return v; - + V oldValue = null; + if (e != null) { + oldValue = e.value; + e.value = newValue; + } + return oldValue; } finally { unlock(); } @@ -357,37 +416,38 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> lock(); try { int c = count; - HashEntry[] tab = table; + if (c++ > threshold) // ensure capacity + rehash(); + HashEntry<K,V>[] tab = table; int index = hash & (tab.length - 1); - HashEntry<K,V> first = (HashEntry<K,V>) tab[index]; + HashEntry<K,V> first = tab[index]; + HashEntry<K,V> e = first; + while (e != null && (e.hash != hash || !key.equals(e.key))) + e = e.next; - for (HashEntry<K,V> e = first; e != null; e = (HashEntry<K,V>) e.next) { - if (e.hash == hash && key.equals(e.key)) { - V oldValue = e.value; - if (!onlyIfAbsent) - e.value = value; - ++modCount; - count = c; // write-volatile - return oldValue; - } + V oldValue; + if (e != null) { + oldValue = e.value; + if (!onlyIfAbsent) + e.value = value; } - - tab[index] = new HashEntry<K,V>(hash, key, value, first); - ++modCount; - ++c; - count = c; // write-volatile - if (c > threshold) - setTable(rehash(tab)); - return null; + else { + oldValue = null; + ++modCount; + tab[index] = new HashEntry<K,V>(key, hash, first, value); + count = c; // write-volatile + } + return oldValue; } finally { unlock(); } } - HashEntry[] rehash(HashEntry[] oldTable) { + void rehash() { + HashEntry<K,V>[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity >= MAXIMUM_CAPACITY) - return oldTable; + return; /* * Reclassify nodes in each list to new Map. Because we are @@ -403,12 +463,13 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * right now. */ - HashEntry[] newTable = new HashEntry[oldCapacity << 1]; + HashEntry<K,V>[] newTable = HashEntry.newArray(oldCapacity<<1); + threshold = (int)(newTable.length * loadFactor); int sizeMask = newTable.length - 1; for (int i = 0; i < oldCapacity ; i++) { // We need to guarantee that any existing reads of old Map can // proceed. So we cannot yet null out each bin. - HashEntry<K,V> e = (HashEntry<K,V>)oldTable[i]; + HashEntry<K,V> e = oldTable[i]; if (e != null) { HashEntry<K,V> next = e.next; @@ -436,15 +497,14 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> // Clone all remaining nodes for (HashEntry<K,V> p = e; p != lastRun; p = p.next) { int k = p.hash & sizeMask; - newTable[k] = new HashEntry<K,V>(p.hash, - p.key, - p.value, - (HashEntry<K,V>) newTable[k]); + HashEntry<K,V> n = newTable[k]; + newTable[k] = new HashEntry<K,V>(p.key, p.hash, + n, p.value); } } } } - return newTable; + table = newTable; } /** @@ -453,33 +513,31 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> V remove(Object key, int hash, Object value) { lock(); try { - int c = count; - HashEntry[] tab = table; + int c = count - 1; + HashEntry<K,V>[] tab = table; int index = hash & (tab.length - 1); - HashEntry<K,V> first = (HashEntry<K,V>)tab[index]; - + HashEntry<K,V> first = tab[index]; HashEntry<K,V> e = first; - for (;;) { - if (e == null) - return null; - if (e.hash == hash && key.equals(e.key)) - break; + while (e != null && (e.hash != hash || !key.equals(e.key))) e = e.next; - } - V oldValue = e.value; - if (value != null && !value.equals(oldValue)) - return null; - - // All entries following removed node can stay in list, but - // all preceding ones need to be cloned. - HashEntry<K,V> newFirst = e.next; - for (HashEntry<K,V> p = first; p != e; p = p.next) - newFirst = new HashEntry<K,V>(p.hash, p.key, - p.value, newFirst); - tab[index] = newFirst; - ++modCount; - count = c-1; // write-volatile + V oldValue = null; + if (e != null) { + V v = e.value; + if (value == null || value.equals(v)) { + oldValue = v; + // All entries following removed node can stay + // in list, but all preceding ones need to be + // cloned. + ++modCount; + HashEntry<K,V> newFirst = e.next; + for (HashEntry<K,V> p = first; p != e; p = p.next) + newFirst = new HashEntry<K,V>(p.key, p.hash, + newFirst, p.value); + tab[index] = newFirst; + count = c; // write-volatile + } + } return oldValue; } finally { unlock(); @@ -487,50 +545,37 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } void clear() { - lock(); - try { - HashEntry[] tab = table; - for (int i = 0; i < tab.length ; i++) - tab[i] = null; - ++modCount; - count = 0; // write-volatile - } finally { - unlock(); + if (count != 0) { + lock(); + try { + HashEntry<K,V>[] tab = table; + for (int i = 0; i < tab.length ; i++) + tab[i] = null; + ++modCount; + count = 0; // write-volatile + } finally { + unlock(); + } } } } - /** - * ConcurrentHashMap list entry. Note that this is never exported - * out as a user-visible Map.Entry - */ - static final class HashEntry<K,V> { - final K key; - V value; - final int hash; - final HashEntry<K,V> next; - - HashEntry(int hash, K key, V value, HashEntry<K,V> next) { - this.value = value; - this.hash = hash; - this.key = key; - this.next = next; - } - } /* ---------------- Public operations -------------- */ /** * Creates a new, empty map with the specified initial - * capacity and the specified load factor. + * capacity, load factor and concurrency level. * * @param initialCapacity the initial capacity. The implementation * performs internal sizing to accommodate this many elements. * @param loadFactor the load factor threshold, used to control resizing. + * Resizing may be performed when the average number of elements per + * bin exceeds this threshold. * @param concurrencyLevel the estimated number of concurrently * updating threads. The implementation performs internal sizing - * to try to accommodate this many threads. + * to try to accommodate this many threads. * @throws IllegalArgumentException if the initial capacity is * negative or the load factor or concurrencyLevel are * nonpositive. @@ -552,7 +597,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } segmentShift = 32 - sshift; segmentMask = ssize - 1; - this.segments = new Segment[ssize]; + this.segments = Segment.newArray(ssize); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; @@ -568,44 +613,50 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } /** - * Creates a new, empty map with the specified initial - * capacity, and with default load factor and concurrencyLevel. + * Creates a new, empty map with the specified initial capacity, + * and with default load factor (0.75) and concurrencyLevel (16). * - * @param initialCapacity The implementation performs internal - * sizing to accommodate this many elements. + * @param initialCapacity the initial capacity. The implementation + * performs internal sizing to accommodate this many elements. * @throws IllegalArgumentException if the initial capacity of * elements is negative. */ public ConcurrentHashMap(int initialCapacity) { - this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS); + this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL); } /** - * Creates a new, empty map with a default initial capacity, - * load factor, and concurrencyLevel. + * Creates a new, empty map with a default initial capacity (16), + * load factor (0.75) and concurrencyLevel (16). */ public ConcurrentHashMap() { - this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS); + this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL); } /** - * Creates a new map with the same mappings as the given map. The - * map is created with a capacity of twice the number of mappings in - * the given map or 11 (whichever is greater), and a default load factor. - * @param t the map + * Creates a new map with the same mappings as the given map. + * The map is created with a capacity of 1.5 times the number + * of mappings in the given map or 16 (whichever is greater), + * and a default load factor (0.75) and concurrencyLevel (16). + * + * @param m the map */ - public ConcurrentHashMap(Map<? extends K, ? extends V> t) { - this(Math.max((int) (t.size() / DEFAULT_LOAD_FACTOR) + 1, - 11), - DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS); - putAll(t); + public ConcurrentHashMap(Map<? extends K, ? extends V> m) { + this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, + DEFAULT_INITIAL_CAPACITY), + DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL); + putAll(m); } - // inherit Map javadoc + /** + * Returns <tt>true</tt> if this map contains no key-value mappings. + * + * @return <tt>true</tt> if this map contains no key-value mappings + */ public boolean isEmpty() { - final Segment[] segments = this.segments; + final Segment<K,V>[] segments = this.segments; /* - * We need to keep track of per-segment modCounts to avoid ABA + * We keep track of per-segment modCounts to avoid ABA * problems in which an element in one segment was added and * in another removed during traversal, in which case the * table was never actually empty at any point. Note the @@ -618,7 +669,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> for (int i = 0; i < segments.length; ++i) { if (segments[i].count != 0) return false; - else + else mcsum += mc[i] = segments[i].modCount; } // If mcsum happens to be zero, then we know we got a snapshot @@ -627,25 +678,35 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> if (mcsum != 0) { for (int i = 0; i < segments.length; ++i) { if (segments[i].count != 0 || - mc[i] != segments[i].modCount) + mc[i] != segments[i].modCount) return false; } } return true; } - // inherit Map javadoc + /** + * Returns the number of key-value mappings in this map. If the + * map contains more than <tt>Integer.MAX_VALUE</tt> elements, returns + * <tt>Integer.MAX_VALUE</tt>. + * + * @return the number of key-value mappings in this map + */ public int size() { - final Segment[] segments = this.segments; + final Segment<K,V>[] segments = this.segments; + long sum = 0; + long check = 0; int[] mc = new int[segments.length]; - for (;;) { - long sum = 0; + // Try a few times to get accurate count. On failure due to + // continuous async changes in table, resort to locking. + for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) { + check = 0; + sum = 0; int mcsum = 0; for (int i = 0; i < segments.length; ++i) { sum += segments[i].count; mcsum += mc[i] = segments[i].modCount; } - int check = 0; if (mcsum != 0) { for (int i = 0; i < segments.length; ++i) { check += segments[i].count; @@ -655,43 +716,51 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } } } - if (check == sum) { - if (sum > Integer.MAX_VALUE) - return Integer.MAX_VALUE; - else - return (int)sum; - } + if (check == sum) + break; + } + if (check != sum) { // Resort to locking all segments + sum = 0; + for (int i = 0; i < segments.length; ++i) + segments[i].lock(); + for (int i = 0; i < segments.length; ++i) + sum += segments[i].count; + for (int i = 0; i < segments.length; ++i) + segments[i].unlock(); } + if (sum > Integer.MAX_VALUE) + return Integer.MAX_VALUE; + else + return (int)sum; } - /** - * Returns the value to which the specified key is mapped in this table. + * Returns the value to which the specified key is mapped, + * or {@code null} if this map contains no mapping for the key. + * + * <p>More formally, if this map contains a mapping from a key + * {@code k} to a value {@code v} such that {@code key.equals(k)}, + * then this method returns {@code v}; otherwise it returns + * {@code null}. (There can be at most one such mapping.) * - * @param key a key in the table. - * @return the value to which the key is mapped in this table; - * <tt>null</tt> if the key is not mapped to any value in - * this table. - * @throws NullPointerException if the key is - * <tt>null</tt>. + * @throws NullPointerException if the specified key is null */ public V get(Object key) { - int hash = hash(key); // throws NullPointerException if key null + int hash = hash(key.hashCode()); return segmentFor(hash).get(key, hash); } /** * Tests if the specified object is a key in this table. * - * @param key possible key. - * @return <tt>true</tt> if and only if the specified object - * is a key in this table, as determined by the - * <tt>equals</tt> method; <tt>false</tt> otherwise. - * @throws NullPointerException if the key is - * <tt>null</tt>. + * @param key possible key + * @return <tt>true</tt> if and only if the specified object + * is a key in this table, as determined by the + * <tt>equals</tt> method; <tt>false</tt> otherwise. + * @throws NullPointerException if the specified key is null */ public boolean containsKey(Object key) { - int hash = hash(key); // throws NullPointerException if key null + int hash = hash(key.hashCode()); return segmentFor(hash).containsKey(key, hash); } @@ -701,18 +770,22 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * traversal of the hash table, and so is much slower than * method <tt>containsKey</tt>. * - * @param value value whose presence in this map is to be tested. + * @param value value whose presence in this map is to be tested * @return <tt>true</tt> if this map maps one or more keys to the - * specified value. - * @throws NullPointerException if the value is <tt>null</tt>. + * specified value + * @throws NullPointerException if the specified value is null */ public boolean containsValue(Object value) { if (value == null) throw new NullPointerException(); - final Segment[] segments = this.segments; + // See explanation of modCount use above + + final Segment<K,V>[] segments = this.segments; int[] mc = new int[segments.length]; - for (;;) { + + // Try a few times without locking + for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) { int sum = 0; int mcsum = 0; for (int i = 0; i < segments.length; ++i) { @@ -734,287 +807,217 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> if (cleanSweep) return false; } + // Resort to locking all segments + for (int i = 0; i < segments.length; ++i) + segments[i].lock(); + boolean found = false; + try { + for (int i = 0; i < segments.length; ++i) { + if (segments[i].containsValue(value)) { + found = true; + break; + } + } + } finally { + for (int i = 0; i < segments.length; ++i) + segments[i].unlock(); + } + return found; } /** * Legacy method testing if some key maps into the specified value * in this table. This method is identical in functionality to - * {@link #containsValue}, and exists solely to ensure + * {@link #containsValue}, and exists solely to ensure * full compatibility with class {@link java.util.Hashtable}, * which supported this method prior to introduction of the * Java Collections framework. - * @param value a value to search for. - * @return <tt>true</tt> if and only if some key maps to the - * <tt>value</tt> argument in this table as - * determined by the <tt>equals</tt> method; - * <tt>false</tt> otherwise. - * @throws NullPointerException if the value is <tt>null</tt>. + * @param value a value to search for + * @return <tt>true</tt> if and only if some key maps to the + * <tt>value</tt> argument in this table as + * determined by the <tt>equals</tt> method; + * <tt>false</tt> otherwise + * @throws NullPointerException if the specified value is null */ public boolean contains(Object value) { return containsValue(value); } /** - * Maps the specified <tt>key</tt> to the specified - * <tt>value</tt> in this table. Neither the key nor the - * value can be <tt>null</tt>. + * Maps the specified key to the specified value in this table. + * Neither the key nor the value can be null. * * <p> The value can be retrieved by calling the <tt>get</tt> method * with a key that is equal to the original key. * - * @param key the table key. - * @param value the value. - * @return the previous value of the specified key in this table, - * or <tt>null</tt> if it did not have one. - * @throws NullPointerException if the key or value is - * <tt>null</tt>. + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previous value associated with <tt>key</tt>, or + * <tt>null</tt> if there was no mapping for <tt>key</tt> + * @throws NullPointerException if the specified key or value is null */ public V put(K key, V value) { if (value == null) throw new NullPointerException(); - int hash = hash(key); + int hash = hash(key.hashCode()); return segmentFor(hash).put(key, hash, value, false); } /** - * If the specified key is not already associated - * with a value, associate it with the given value. - * This is equivalent to - * <pre> - * if (!map.containsKey(key)) - * return map.put(key, value); - * else - * return map.get(key); - * </pre> - * Except that the action is performed atomically. - * @param key key with which the specified value is to be associated. - * @param value value to be associated with the specified key. - * @return previous value associated with specified key, or <tt>null</tt> - * if there was no mapping for key. A <tt>null</tt> return can - * also indicate that the map previously associated <tt>null</tt> - * with the specified key, if the implementation supports - * <tt>null</tt> values. - * - * @throws UnsupportedOperationException if the <tt>put</tt> operation is - * not supported by this map. - * @throws ClassCastException if the class of the specified key or value - * prevents it from being stored in this map. - * @throws NullPointerException if the specified key or value is - * <tt>null</tt>. + * {@inheritDoc} * - **/ + * @return the previous value associated with the specified key, + * or <tt>null</tt> if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null + */ public V putIfAbsent(K key, V value) { if (value == null) throw new NullPointerException(); - int hash = hash(key); + int hash = hash(key.hashCode()); return segmentFor(hash).put(key, hash, value, true); } - /** * Copies all of the mappings from the specified map to this one. - * * These mappings replace any mappings that this map had for any of the - * keys currently in the specified Map. + * keys currently in the specified map. * - * @param t Mappings to be stored in this map. + * @param m mappings to be stored in this map */ - public void putAll(Map<? extends K, ? extends V> t) { - for (Iterator<? extends Map.Entry<? extends K, ? extends V>> it = (Iterator<? extends Map.Entry<? extends K, ? extends V>>) t.entrySet().iterator(); it.hasNext(); ) { - Entry<? extends K, ? extends V> e = it.next(); + public void putAll(Map<? extends K, ? extends V> m) { + for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue()); - } } /** - * Removes the key (and its corresponding value) from this - * table. This method does nothing if the key is not in the table. + * Removes the key (and its corresponding value) from this map. + * This method does nothing if the key is not in the map. * - * @param key the key that needs to be removed. - * @return the value to which the key had been mapped in this table, - * or <tt>null</tt> if the key did not have a mapping. - * @throws NullPointerException if the key is - * <tt>null</tt>. + * @param key the key that needs to be removed + * @return the previous value associated with <tt>key</tt>, or + * <tt>null</tt> if there was no mapping for <tt>key</tt> + * @throws NullPointerException if the specified key is null */ public V remove(Object key) { - int hash = hash(key); + int hash = hash(key.hashCode()); return segmentFor(hash).remove(key, hash, null); } /** - * Remove entry for key only if currently mapped to given value. - * Acts as - * <pre> - * if (map.get(key).equals(value)) { - * map.remove(key); - * return true; - * } else return false; - * </pre> - * except that the action is performed atomically. - * @param key key with which the specified value is associated. - * @param value value associated with the specified key. - * @return true if the value was removed - * @throws NullPointerException if the specified key is - * <tt>null</tt>. + * {@inheritDoc} + * + * @throws NullPointerException if the specified key is null */ public boolean remove(Object key, Object value) { - int hash = hash(key); + int hash = hash(key.hashCode()); + if (value == null) + return false; return segmentFor(hash).remove(key, hash, value) != null; } - /** - * Replace entry for key only if currently mapped to given value. - * Acts as - * <pre> - * if (map.get(key).equals(oldValue)) { - * map.put(key, newValue); - * return true; - * } else return false; - * </pre> - * except that the action is performed atomically. - * @param key key with which the specified value is associated. - * @param oldValue value expected to be associated with the specified key. - * @param newValue value to be associated with the specified key. - * @return true if the value was replaced - * @throws NullPointerException if the specified key or values are - * <tt>null</tt>. + * {@inheritDoc} + * + * @throws NullPointerException if any of the arguments are null */ public boolean replace(K key, V oldValue, V newValue) { if (oldValue == null || newValue == null) throw new NullPointerException(); - int hash = hash(key); + int hash = hash(key.hashCode()); return segmentFor(hash).replace(key, hash, oldValue, newValue); } /** - * Replace entry for key only if currently mapped to some value. - * Acts as - * <pre> - * if ((map.containsKey(key)) { - * return map.put(key, value); - * } else return null; - * </pre> - * except that the action is performed atomically. - * @param key key with which the specified value is associated. - * @param value value to be associated with the specified key. - * @return previous value associated with specified key, or <tt>null</tt> - * if there was no mapping for key. - * @throws NullPointerException if the specified key or value is - * <tt>null</tt>. + * {@inheritDoc} + * + * @return the previous value associated with the specified key, + * or <tt>null</tt> if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null */ public V replace(K key, V value) { if (value == null) throw new NullPointerException(); - int hash = hash(key); + int hash = hash(key.hashCode()); return segmentFor(hash).replace(key, hash, value); } - /** - * Removes all mappings from this map. + * Removes all of the mappings from this map. */ public void clear() { for (int i = 0; i < segments.length; ++i) segments[i].clear(); } - - // BEGIN android-removed - // /** - // * Returns a shallow copy of this - // * <tt>ConcurrentHashMap</tt> instance: the keys and - // * values themselves are not cloned. - // * - // * @return a shallow copy of this map. - // */ - // public Object clone() { - // // We cannot call super.clone, since it would share final - // // segments array, and there's no way to reassign finals. - // - // float lf = segments[0].loadFactor; - // int segs = segments.length; - // int cap = (int)(size() / lf); - // if (cap < segs) cap = segs; - // ConcurrentHashMap<K,V> t = new ConcurrentHashMap<K,V>(cap, lf, segs); - // t.putAll(this); - // return t; - // } - // END android-changed - /** - * Returns a set view of the keys contained in this map. The set is - * backed by the map, so changes to the map are reflected in the set, and - * vice-versa. The set supports element removal, which removes the - * corresponding mapping from this map, via the <tt>Iterator.remove</tt>, - * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and - * <tt>clear</tt> operations. It does not support the <tt>add</tt> or + * Returns a {@link Set} view of the keys contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. The set supports element + * removal, which removes the corresponding mapping from this map, + * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>, + * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> + * operations. It does not support the <tt>add</tt> or * <tt>addAll</tt> operations. - * The returned <tt>iterator</tt> is a "weakly consistent" iterator that - * will never throw {@link java.util.ConcurrentModificationException}, + * + * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, * and guarantees to traverse elements as they existed upon * construction of the iterator, and may (but is not guaranteed to) * reflect any modifications subsequent to construction. - * - * @return a set view of the keys contained in this map. */ public Set<K> keySet() { Set<K> ks = keySet; return (ks != null) ? ks : (keySet = new KeySet()); } - /** - * Returns a collection view of the values contained in this map. The - * collection is backed by the map, so changes to the map are reflected in - * the collection, and vice-versa. The collection supports element - * removal, which removes the corresponding mapping from this map, via the - * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>, - * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations. - * It does not support the <tt>add</tt> or <tt>addAll</tt> operations. - * The returned <tt>iterator</tt> is a "weakly consistent" iterator that - * will never throw {@link java.util.ConcurrentModificationException}, + * Returns a {@link Collection} view of the values contained in this map. + * The collection is backed by the map, so changes to the map are + * reflected in the collection, and vice-versa. The collection + * supports element removal, which removes the corresponding + * mapping from this map, via the <tt>Iterator.remove</tt>, + * <tt>Collection.remove</tt>, <tt>removeAll</tt>, + * <tt>retainAll</tt>, and <tt>clear</tt> operations. It does not + * support the <tt>add</tt> or <tt>addAll</tt> operations. + * + * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, * and guarantees to traverse elements as they existed upon * construction of the iterator, and may (but is not guaranteed to) * reflect any modifications subsequent to construction. - * - * @return a collection view of the values contained in this map. */ public Collection<V> values() { Collection<V> vs = values; return (vs != null) ? vs : (values = new Values()); } - /** - * Returns a collection view of the mappings contained in this map. Each - * element in the returned collection is a <tt>Map.Entry</tt>. The - * collection is backed by the map, so changes to the map are reflected in - * the collection, and vice-versa. The collection supports element - * removal, which removes the corresponding mapping from the map, via the - * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>, - * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations. - * It does not support the <tt>add</tt> or <tt>addAll</tt> operations. - * The returned <tt>iterator</tt> is a "weakly consistent" iterator that - * will never throw {@link java.util.ConcurrentModificationException}, + * Returns a {@link Set} view of the mappings contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. The set supports element + * removal, which removes the corresponding mapping from the map, + * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>, + * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> + * operations. It does not support the <tt>add</tt> or + * <tt>addAll</tt> operations. + * + * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, * and guarantees to traverse elements as they existed upon * construction of the iterator, and may (but is not guaranteed to) * reflect any modifications subsequent to construction. - * - * @return a collection view of the mappings contained in this map. */ public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> es = entrySet; - return (es != null) ? es : (entrySet = (Set<Map.Entry<K,V>>) (Set) new EntrySet()); + return (es != null) ? es : (entrySet = new EntrySet()); } - /** * Returns an enumeration of the keys in this table. * - * @return an enumeration of the keys in this table. - * @see #keySet + * @return an enumeration of the keys in this table + * @see #keySet() */ public Enumeration<K> keys() { return new KeyIterator(); @@ -1023,8 +1026,8 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> /** * Returns an enumeration of the values in this table. * - * @return an enumeration of the values in this table. - * @see #values + * @return an enumeration of the values in this table + * @see #values() */ public Enumeration<V> elements() { return new ValueIterator(); @@ -1035,7 +1038,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> abstract class HashIterator { int nextSegmentIndex; int nextTableIndex; - HashEntry[] currentTable; + HashEntry<K,V>[] currentTable; HashEntry<K, V> nextEntry; HashEntry<K, V> lastReturned; @@ -1052,16 +1055,16 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> return; while (nextTableIndex >= 0) { - if ( (nextEntry = (HashEntry<K,V>)currentTable[nextTableIndex--]) != null) + if ( (nextEntry = currentTable[nextTableIndex--]) != null) return; } while (nextSegmentIndex >= 0) { - Segment<K,V> seg = (Segment<K,V>)segments[nextSegmentIndex--]; + Segment<K,V> seg = segments[nextSegmentIndex--]; if (seg.count != 0) { currentTable = seg.table; for (int j = currentTable.length - 1; j >= 0; --j) { - if ( (nextEntry = (HashEntry<K,V>)currentTable[j]) != null) { + if ( (nextEntry = currentTable[j]) != null) { nextTableIndex = j - 1; return; } @@ -1088,81 +1091,58 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } } - final class KeyIterator extends HashIterator implements Iterator<K>, Enumeration<K> { - public K next() { return super.nextEntry().key; } + final class KeyIterator + extends HashIterator + implements Iterator<K>, Enumeration<K> + { + public K next() { return super.nextEntry().key; } public K nextElement() { return super.nextEntry().key; } } - final class ValueIterator extends HashIterator implements Iterator<V>, Enumeration<V> { - public V next() { return super.nextEntry().value; } + final class ValueIterator + extends HashIterator + implements Iterator<V>, Enumeration<V> + { + public V next() { return super.nextEntry().value; } public V nextElement() { return super.nextEntry().value; } } - - /** - * Entry iterator. Exported Entry objects must write-through - * changes in setValue, even if the nodes have been cloned. So we - * cannot return internal HashEntry objects. Instead, the iterator - * itself acts as a forwarding pseudo-entry. + * Custom Entry class used by EntryIterator.next(), that relays + * setValue changes to the underlying map. */ - final class EntryIterator extends HashIterator implements Map.Entry<K,V>, Iterator<Entry<K,V>> { - public Map.Entry<K,V> next() { - nextEntry(); - return this; - } - - public K getKey() { - if (lastReturned == null) - throw new IllegalStateException("Entry was removed"); - return lastReturned.key; - } - - public V getValue() { - if (lastReturned == null) - throw new IllegalStateException("Entry was removed"); - return ConcurrentHashMap.this.get(lastReturned.key); + final class WriteThroughEntry + extends SimpleEntry<K,V> + { + WriteThroughEntry(K k, V v) { + super(k,v); } + /** + * Set our entry's value and write through to the map. The + * value to return is somewhat arbitrary here. Since a + * WriteThroughEntry does not necessarily track asynchronous + * changes, the most recent "previous" value could be + * different from what we return (or could even have been + * removed in which case the put will re-establish). We do not + * and cannot guarantee more. + */ public V setValue(V value) { - if (lastReturned == null) - throw new IllegalStateException("Entry was removed"); - return ConcurrentHashMap.this.put(lastReturned.key, value); - } - - public boolean equals(Object o) { - // If not acting as entry, just use default. - if (lastReturned == null) - return super.equals(o); - if (!(o instanceof Map.Entry)) - return false; - Map.Entry e = (Map.Entry)o; - return eq(getKey(), e.getKey()) && eq(getValue(), e.getValue()); - } - - public int hashCode() { - // If not acting as entry, just use default. - if (lastReturned == null) - return super.hashCode(); - - Object k = getKey(); - Object v = getValue(); - return ((k == null) ? 0 : k.hashCode()) ^ - ((v == null) ? 0 : v.hashCode()); - } - - public String toString() { - // If not acting as entry, just use default. - if (lastReturned == null) - return super.toString(); - else - return getKey() + "=" + getValue(); + if (value == null) throw new NullPointerException(); + V v = super.setValue(value); + ConcurrentHashMap.this.put(getKey(), value); + return v; } + } - boolean eq(Object o1, Object o2) { - return (o1 == null ? o2 == null : o1.equals(o2)); + final class EntryIterator + extends HashIterator + implements Iterator<Entry<K,V>> + { + public Map.Entry<K,V> next() { + HashEntry<K,V> e = super.nextEntry(); + return new WriteThroughEntry(e.key, e.value); } - } final class KeySet extends AbstractSet<K> { @@ -1172,6 +1152,9 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> public int size() { return ConcurrentHashMap.this.size(); } + public boolean isEmpty() { + return ConcurrentHashMap.this.isEmpty(); + } public boolean contains(Object o) { return ConcurrentHashMap.this.containsKey(o); } @@ -1190,6 +1173,9 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> public int size() { return ConcurrentHashMap.this.size(); } + public boolean isEmpty() { + return ConcurrentHashMap.this.isEmpty(); + } public boolean contains(Object o) { return ConcurrentHashMap.this.containsValue(o); } @@ -1205,44 +1191,32 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; - Map.Entry<K,V> e = (Map.Entry<K,V>)o; + Map.Entry<?,?> e = (Map.Entry<?,?>)o; V v = ConcurrentHashMap.this.get(e.getKey()); return v != null && v.equals(e.getValue()); } public boolean remove(Object o) { if (!(o instanceof Map.Entry)) return false; - Map.Entry<K,V> e = (Map.Entry<K,V>)o; + Map.Entry<?,?> e = (Map.Entry<?,?>)o; return ConcurrentHashMap.this.remove(e.getKey(), e.getValue()); } public int size() { return ConcurrentHashMap.this.size(); } + public boolean isEmpty() { + return ConcurrentHashMap.this.isEmpty(); + } public void clear() { ConcurrentHashMap.this.clear(); } - public Object[] toArray() { - // Since we don't ordinarily have distinct Entry objects, we - // must pack elements using exportable SimpleEntry - Collection<Map.Entry<K,V>> c = new ArrayList<Map.Entry<K,V>>(size()); - for (Iterator<Map.Entry<K,V>> i = iterator(); i.hasNext(); ) - c.add(new SimpleEntry<K,V>(i.next())); - return c.toArray(); - } - public <T> T[] toArray(T[] a) { - Collection<Map.Entry<K,V>> c = new ArrayList<Map.Entry<K,V>>(size()); - for (Iterator<Map.Entry<K,V>> i = iterator(); i.hasNext(); ) - c.add(new SimpleEntry<K,V>(i.next())); - return c.toArray(a); - } - } /** * This duplicates java.util.AbstractMap.SimpleEntry until this class * is made accessible. */ - static final class SimpleEntry<K,V> implements Entry<K,V> { + static class SimpleEntry<K,V> implements Entry<K,V> { K key; V value; @@ -1279,7 +1253,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> public int hashCode() { return ((key == null) ? 0 : key.hashCode()) ^ - ((value == null) ? 0 : value.hashCode()); + ((value == null) ? 0 : value.hashCode()); } public String toString() { @@ -1291,12 +1265,11 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } } - /* ---------------- Serialization Support -------------- */ + /* ---------------- Serialization Support -------------- */ /** - * Save the state of the <tt>ConcurrentHashMap</tt> - * instance to a stream (i.e., - * serialize it). + * Save the state of the <tt>ConcurrentHashMap</tt> instance to a + * stream (i.e., serialize it). * @param s the stream * @serialData * the key (Object) and value (Object) @@ -1307,12 +1280,12 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> s.defaultWriteObject(); for (int k = 0; k < segments.length; ++k) { - Segment<K,V> seg = (Segment<K,V>)segments[k]; + Segment<K,V> seg = segments[k]; seg.lock(); try { - HashEntry[] tab = seg.table; + HashEntry<K,V>[] tab = seg.table; for (int i = 0; i < tab.length; ++i) { - for (HashEntry<K,V> e = (HashEntry<K,V>)tab[i]; e != null; e = e.next) { + for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) { s.writeObject(e.key); s.writeObject(e.value); } @@ -1326,9 +1299,8 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } /** - * Reconstitute the <tt>ConcurrentHashMap</tt> - * instance from a stream (i.e., - * deserialize it). + * Reconstitute the <tt>ConcurrentHashMap</tt> instance from a + * stream (i.e., deserialize it). * @param s the stream */ private void readObject(java.io.ObjectInputStream s) @@ -1350,4 +1322,3 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } } } - diff --git a/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java b/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java index 794142d..2253823 100644 --- a/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java +++ b/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java @@ -14,7 +14,7 @@ import java.util.concurrent.atomic.*; // END android-note /** - * An unbounded thread-safe {@linkplain Queue queue} based on linked nodes. + * An unbounded thread-safe {@linkplain Queue queue} based on linked nodes. * This queue orders elements FIFO (first-in-first-out). * The <em>head</em> of the queue is that element that has been on the * queue the longest time. @@ -22,23 +22,31 @@ import java.util.concurrent.atomic.*; * queue the shortest time. New elements * are inserted at the tail of the queue, and the queue retrieval * operations obtain elements at the head of the queue. - * A <tt>ConcurrentLinkedQueue</tt> is an appropriate choice when + * A {@code ConcurrentLinkedQueue} is an appropriate choice when * many threads will share access to a common collection. - * This queue does not permit <tt>null</tt> elements. + * This queue does not permit {@code null} elements. * - * <p>This implementation employs an efficient "wait-free" + * <p>This implementation employs an efficient "wait-free" * algorithm based on one described in <a * href="http://www.cs.rochester.edu/u/michael/PODC96.html"> Simple, * Fast, and Practical Non-Blocking and Blocking Concurrent Queue * Algorithms</a> by Maged M. Michael and Michael L. Scott. * - * <p>Beware that, unlike in most collections, the <tt>size</tt> method + * <p>Beware that, unlike in most collections, the {@code size} method * is <em>NOT</em> a constant-time operation. Because of the * asynchronous nature of these queues, determining the current number * of elements requires a traversal of the elements. * - * <p>This class implements all of the <em>optional</em> methods - * of the {@link Collection} and {@link Iterator} interfaces. + * <p>This class and its iterator implement all of the + * <em>optional</em> methods of the {@link Collection} and {@link + * Iterator} interfaces. + * + * <p>Memory consistency effects: As with other concurrent + * collections, actions in a thread prior to placing an object into a + * {@code ConcurrentLinkedQueue} + * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> + * actions subsequent to the access or removal of that element from + * the {@code ConcurrentLinkedQueue} in another thread. * * @since 1.5 * @author Doug Lea @@ -50,68 +58,126 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> private static final long serialVersionUID = 196745693267521676L; /* - * This is a straight adaptation of Michael & Scott algorithm. - * For explanation, read the paper. The only (minor) algorithmic - * difference is that this version supports lazy deletion of - * internal nodes (method remove(Object)) -- remove CAS'es item - * fields to null. The normal queue operations unlink but then - * pass over nodes with null item fields. Similarly, iteration - * methods ignore those with nulls. + * This is a modification of the Michael & Scott algorithm, + * adapted for a garbage-collected environment, with support for + * interior node deletion (to support remove(Object)). For + * explanation, read the paper. + * + * Note that like most non-blocking algorithms in this package, + * this implementation relies on the fact that in garbage + * collected systems, there is no possibility of ABA problems due + * to recycled nodes, so there is no need to use "counted + * pointers" or related techniques seen in versions used in + * non-GC'ed settings. + * + * The fundamental invariants are: + * - There is exactly one (last) Node with a null next reference, + * which is CASed when enqueueing. This last Node can be + * reached in O(1) time from tail, but tail is merely an + * optimization - it can always be reached in O(N) time from + * head as well. + * - The elements contained in the queue are the non-null items in + * Nodes that are reachable from head. CASing the item + * reference of a Node to null atomically removes it from the + * queue. Reachability of all elements from head must remain + * true even in the case of concurrent modifications that cause + * head to advance. A dequeued Node may remain in use + * indefinitely due to creation of an Iterator or simply a + * poll() that has lost its time slice. + * + * The above might appear to imply that all Nodes are GC-reachable + * from a predecessor dequeued Node. That would cause two problems: + * - allow a rogue Iterator to cause unbounded memory retention + * - cause cross-generational linking of old Nodes to new Nodes if + * a Node was tenured while live, which generational GCs have a + * hard time dealing with, causing repeated major collections. + * However, only non-deleted Nodes need to be reachable from + * dequeued Nodes, and reachability does not necessarily have to + * be of the kind understood by the GC. We use the trick of + * linking a Node that has just been dequeued to itself. Such a + * self-link implicitly means to advance to head. + * + * Both head and tail are permitted to lag. In fact, failing to + * update them every time one could is a significant optimization + * (fewer CASes). This is controlled by local "hops" variables + * that only trigger helping-CASes after experiencing multiple + * lags. + * + * Since head and tail are updated concurrently and independently, + * it is possible for tail to lag behind head (why not)? + * + * CASing a Node's item reference to null atomically removes the + * element from the queue. Iterators skip over Nodes with null + * items. Prior implementations of this class had a race between + * poll() and remove(Object) where the same element would appear + * to be successfully removed by two concurrent operations. The + * method remove(Object) also lazily unlinks deleted Nodes, but + * this is merely an optimization. + * + * When constructing a Node (before enqueuing it) we avoid paying + * for a volatile write to item by using lazySet instead of a + * normal write. This allows the cost of enqueue to be + * "one-and-a-half" CASes. + * + * Both head and tail may or may not point to a Node with a + * non-null item. If the queue is empty, all items must of course + * be null. Upon creation, both head and tail refer to a dummy + * Node with null item. Both head and tail are only updated using + * CAS, so they never regress, although again this is merely an + * optimization. */ - private static class Node<E> { private volatile E item; private volatile Node<E> next; - - private static final - AtomicReferenceFieldUpdater<Node, Node> + + private static final + AtomicReferenceFieldUpdater<Node, Node> nextUpdater = AtomicReferenceFieldUpdater.newUpdater (Node.class, Node.class, "next"); - private static final - AtomicReferenceFieldUpdater<Node, Object> + private static final + AtomicReferenceFieldUpdater<Node, Object> itemUpdater = AtomicReferenceFieldUpdater.newUpdater (Node.class, Object.class, "item"); - - Node(E x) { item = x; } - - Node(E x, Node<E> n) { item = x; next = n; } - + + + Node(E item) { setItem(item); } + E getItem() { return item; } - + boolean casItem(E cmp, E val) { return itemUpdater.compareAndSet(this, cmp, val); } - + void setItem(E val) { itemUpdater.set(this, val); } - + Node<E> getNext() { return next; } - + boolean casNext(Node<E> cmp, Node<E> val) { return nextUpdater.compareAndSet(this, cmp, val); } - + void setNext(Node<E> val) { nextUpdater.set(this, val); - } - } - private static final - AtomicReferenceFieldUpdater<ConcurrentLinkedQueue, Node> - tailUpdater = + } + + private static final + AtomicReferenceFieldUpdater<ConcurrentLinkedQueue, Node> + tailUpdater = AtomicReferenceFieldUpdater.newUpdater (ConcurrentLinkedQueue.class, Node.class, "tail"); - private static final - AtomicReferenceFieldUpdater<ConcurrentLinkedQueue, Node> - headUpdater = + private static final + AtomicReferenceFieldUpdater<ConcurrentLinkedQueue, Node> + headUpdater = AtomicReferenceFieldUpdater.newUpdater (ConcurrentLinkedQueue.class, Node.class, "head"); @@ -124,118 +190,141 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> } + /** - * Pointer to header node, initialized to a dummy node. The first - * actual node is at head.getNext(). + * Pointer to first node, initialized to a dummy node. */ - private transient volatile Node<E> head = new Node<E>(null, null); + private transient volatile Node<E> head = new Node<E>(null); - /** Pointer to last node on list **/ + /** Pointer to last node on list */ private transient volatile Node<E> tail = head; /** - * Creates a <tt>ConcurrentLinkedQueue</tt> that is initially empty. + * Creates a {@code ConcurrentLinkedQueue} that is initially empty. */ public ConcurrentLinkedQueue() {} /** - * Creates a <tt>ConcurrentLinkedQueue</tt> + * Creates a {@code ConcurrentLinkedQueue} * initially containing the elements of the given collection, * added in traversal order of the collection's iterator. * @param c the collection of elements to initially contain - * @throws NullPointerException if <tt>c</tt> or any element within it - * is <tt>null</tt> + * @throws NullPointerException if the specified collection or any + * of its elements are null */ public ConcurrentLinkedQueue(Collection<? extends E> c) { for (Iterator<? extends E> it = c.iterator(); it.hasNext();) add(it.next()); } - // Have to override just to update the javadoc + // Have to override just to update the javadoc /** - * Adds the specified element to the tail of this queue. - * @param o the element to add. - * @return <tt>true</tt> (as per the general contract of - * <tt>Collection.add</tt>). + * Inserts the specified element at the tail of this queue. * - * @throws NullPointerException if the specified element is <tt>null</tt> + * @return {@code true} (as specified by {@link Collection#add}) + * @throws NullPointerException if the specified element is null */ - public boolean add(E o) { - return offer(o); + public boolean add(E e) { + return offer(e); } /** - * Inserts the specified element to the tail of this queue. + * We don't bother to update head or tail pointers if less than + * HOPS links from "true" location. We assume that volatile + * writes are significantly more expensive than volatile reads. + */ + private static final int HOPS = 1; + + /** + * Try to CAS head to p. If successful, repoint old head to itself + * as sentinel for succ(), below. + */ + final void updateHead(Node<E> h, Node<E> p) { + if (h != p && casHead(h, p)) + h.setNext(h); + } + + /** + * Returns the successor of p, or the head node if p.next has been + * linked to self, which will only be true if traversing with a + * stale pointer that is now off the list. + */ + final Node<E> succ(Node<E> p) { + Node<E> next = p.getNext(); + return (p == next) ? head : next; + } + + /** + * Inserts the specified element at the tail of this queue. * - * @param o the element to add. - * @return <tt>true</tt> (as per the general contract of - * <tt>Queue.offer</tt>). - * @throws NullPointerException if the specified element is <tt>null</tt> + * @return {@code true} (as specified by {@link Queue#offer}) + * @throws NullPointerException if the specified element is null */ - public boolean offer(E o) { - if (o == null) throw new NullPointerException(); - Node<E> n = new Node<E>(o, null); - for(;;) { + public boolean offer(E e) { + if (e == null) throw new NullPointerException(); + Node<E> n = new Node<E>(e); + retry: + for (;;) { Node<E> t = tail; - Node<E> s = t.getNext(); - if (t == tail) { - if (s == null) { - if (t.casNext(s, n)) { - casTail(t, n); - return true; - } + Node<E> p = t; + for (int hops = 0; ; hops++) { + Node<E> next = succ(p); + if (next != null) { + if (hops > HOPS && t != tail) + continue retry; + p = next; + } else if (p.casNext(null, n)) { + if (hops >= HOPS) + casTail(t, n); // Failure is OK. + return true; } else { - casTail(t, s); + p = succ(p); } } } } public E poll() { - for (;;) { - Node<E> h = head; - Node<E> t = tail; - Node<E> first = h.getNext(); - if (h == head) { - if (h == t) { - if (first == null) - return null; - else - casTail(t, first); - } else if (casHead(h, first)) { - E item = first.getItem(); - if (item != null) { - first.setItem(null); - return item; - } - // else skip over deleted item, continue loop, + Node<E> h = head; + Node<E> p = h; + for (int hops = 0; ; hops++) { + E item = p.getItem(); + + if (item != null && p.casItem(item, null)) { + if (hops >= HOPS) { + Node<E> q = p.getNext(); + updateHead(h, (q != null) ? q : p); } + return item; } + Node<E> next = succ(p); + if (next == null) { + updateHead(h, p); + break; + } + p = next; } + return null; } - public E peek() { // same as poll except don't remove item + public E peek() { + Node<E> h = head; + Node<E> p = h; + E item; for (;;) { - Node<E> h = head; - Node<E> t = tail; - Node<E> first = h.getNext(); - if (h == head) { - if (h == t) { - if (first == null) - return null; - else - casTail(t, first); - } else { - E item = first.getItem(); - if (item != null) - return item; - else // remove deleted node and continue - casHead(h, first); - } + item = p.getItem(); + if (item != null) + break; + Node<E> next = succ(p); + if (next == null) { + break; } + p = next; } + updateHead(h, p); + return item; } /** @@ -245,46 +334,50 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> * introducing race.) */ Node<E> first() { + Node<E> h = head; + Node<E> p = h; + Node<E> result; for (;;) { - Node<E> h = head; - Node<E> t = tail; - Node<E> first = h.getNext(); - if (h == head) { - if (h == t) { - if (first == null) - return null; - else - casTail(t, first); - } else { - if (first.getItem() != null) - return first; - else // remove deleted node and continue - casHead(h, first); - } + E item = p.getItem(); + if (item != null) { + result = p; + break; } + Node<E> next = succ(p); + if (next == null) { + result = null; + break; + } + p = next; } + updateHead(h, p); + return result; } - + /** + * Returns {@code true} if this queue contains no elements. + * + * @return {@code true} if this queue contains no elements + */ public boolean isEmpty() { return first() == null; } /** * Returns the number of elements in this queue. If this queue - * contains more than <tt>Integer.MAX_VALUE</tt> elements, returns - * <tt>Integer.MAX_VALUE</tt>. + * contains more than {@code Integer.MAX_VALUE} elements, returns + * {@code Integer.MAX_VALUE}. * * <p>Beware that, unlike in most collections, this method is * <em>NOT</em> a constant-time operation. Because of the * asynchronous nature of these queues, determining the current * number of elements requires an O(n) traversal. * - * @return the number of elements in this queue. + * @return the number of elements in this queue */ public int size() { int count = 0; - for (Node<E> p = first(); p != null; p = p.getNext()) { + for (Node<E> p = first(); p != null; p = succ(p)) { if (p.getItem() != null) { // Collections.size() spec says to max out if (++count == Integer.MAX_VALUE) @@ -294,9 +387,17 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> return count; } + /** + * Returns {@code true} if this queue contains the specified element. + * More formally, returns {@code true} if and only if this queue contains + * at least one element {@code e} such that {@code o.equals(e)}. + * + * @param o object to be checked for containment in this queue + * @return {@code true} if this queue contains the specified element + */ public boolean contains(Object o) { if (o == null) return false; - for (Node<E> p = first(); p != null; p = p.getNext()) { + for (Node<E> p = first(); p != null; p = succ(p)) { E item = p.getItem(); if (item != null && o.equals(item)) @@ -305,22 +406,50 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> return false; } + /** + * Removes a single instance of the specified element from this queue, + * if it is present. More formally, removes an element {@code e} such + * that {@code o.equals(e)}, if this queue contains one or more such + * elements. + * Returns {@code true} if this queue contained the specified element + * (or equivalently, if this queue changed as a result of the call). + * + * @param o element to be removed from this queue, if present + * @return {@code true} if this queue changed as a result of the call + */ public boolean remove(Object o) { if (o == null) return false; - for (Node<E> p = first(); p != null; p = p.getNext()) { + Node<E> pred = null; + for (Node<E> p = first(); p != null; p = succ(p)) { E item = p.getItem(); - if (item != null && - o.equals(item) && - p.casItem(item, null)) + if (item != null && o.equals(item) && p.casItem(item, null)) { + Node<E> next = succ(p); + if (pred != null && next != null) + pred.casNext(p, next); return true; + } + pred = p; } return false; } + /** + * Returns an array containing all of the elements in this queue, in + * proper sequence. + * + * <p>The returned array will be "safe" in that no references to it are + * maintained by this queue. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + * <p>This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this queue + */ public Object[] toArray() { // Use ArrayList to deal with resizing. ArrayList<E> al = new ArrayList<E>(); - for (Node<E> p = first(); p != null; p = p.getNext()) { + for (Node<E> p = first(); p != null; p = succ(p)) { E item = p.getItem(); if (item != null) al.add(item); @@ -328,11 +457,48 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> return al.toArray(); } + /** + * Returns an array containing all of the elements in this queue, in + * proper sequence; the runtime type of the returned array is that of + * the specified array. If the queue fits in the specified array, it + * is returned therein. Otherwise, a new array is allocated with the + * runtime type of the specified array and the size of this queue. + * + * <p>If this queue fits in the specified array with room to spare + * (i.e., the array has more elements than this queue), the element in + * the array immediately following the end of the queue is set to + * {@code null}. + * + * <p>Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + * <p>Suppose {@code x} is a queue known to contain only strings. + * The following code can be used to dump the queue into a newly + * allocated array of {@code String}: + * + * <pre> + * String[] y = x.toArray(new String[0]);</pre> + * + * Note that {@code toArray(new Object[0])} is identical in function to + * {@code toArray()}. + * + * @param a the array into which the elements of the queue are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose + * @return an array containing all of the elements in this queue + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this queue + * @throws NullPointerException if the specified array is null + */ + @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { // try to use sent-in array int k = 0; Node<E> p; - for (p = first(); p != null && k < a.length; p = p.getNext()) { + for (p = first(); p != null && k < a.length; p = succ(p)) { E item = p.getItem(); if (item != null) a[k++] = (T)item; @@ -345,23 +511,23 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> // If won't fit, use ArrayList version ArrayList<E> al = new ArrayList<E>(); - for (Node<E> q = first(); q != null; q = q.getNext()) { + for (Node<E> q = first(); q != null; q = succ(q)) { E item = q.getItem(); if (item != null) al.add(item); } - return (T[])al.toArray(a); + return al.toArray(a); } /** * Returns an iterator over the elements in this queue in proper sequence. * The returned iterator is a "weakly consistent" iterator that - * will never throw {@link java.util.ConcurrentModificationException}, + * will never throw {@link ConcurrentModificationException}, * and guarantees to traverse elements as they existed upon * construction of the iterator, and may (but is not guaranteed to) * reflect any modifications subsequent to construction. * - * @return an iterator over the elements in this queue in proper sequence. + * @return an iterator over the elements in this queue in proper sequence */ public Iterator<E> iterator() { return new Itr(); @@ -378,7 +544,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> * that an element exists in hasNext(), we must return it in * the following next() call even if it was in the process of * being removed when hasNext() was called. - **/ + */ private E nextItem; /** @@ -398,7 +564,15 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> lastRet = nextNode; E x = nextItem; - Node<E> p = (nextNode == null)? first() : nextNode.getNext(); + Node<E> pred, p; + if (nextNode == null) { + p = first(); + pred = null; + } else { + pred = nextNode; + p = succ(nextNode); + } + for (;;) { if (p == null) { nextNode = null; @@ -410,8 +584,13 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> nextNode = p; nextItem = item; return x; - } else // skip over nulls - p = p.getNext(); + } else { + // skip over nulls + Node<E> next = succ(p); + if (pred != null && next != null) + pred.casNext(p, next); + p = next; + } } } @@ -436,7 +615,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> /** * Save the state to a stream (that is, serialize it). * - * @serialData All of the elements (each an <tt>E</tt>) in + * @serialData All of the elements (each an {@code E}) in * the proper order, followed by a null * @param s the stream */ @@ -447,7 +626,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> s.defaultWriteObject(); // Write out all elements in the proper order. - for (Node<E> p = first(); p != null; p = p.getNext()) { + for (Node<E> p = first(); p != null; p = succ(p)) { Object item = p.getItem(); if (item != null) s.writeObject(item); @@ -466,10 +645,11 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> throws java.io.IOException, ClassNotFoundException { // Read in capacity, and any hidden stuff s.defaultReadObject(); - head = new Node<E>(null, null); + head = new Node<E>(null); tail = head; // Read in all elements and place in queue for (;;) { + @SuppressWarnings("unchecked") E item = (E)s.readObject(); if (item == null) break; diff --git a/concurrent/src/main/java/java/util/concurrent/ConcurrentMap.java b/concurrent/src/main/java/java/util/concurrent/ConcurrentMap.java index 32dc000..2daebc5 100644 --- a/concurrent/src/main/java/java/util/concurrent/ConcurrentMap.java +++ b/concurrent/src/main/java/java/util/concurrent/ConcurrentMap.java @@ -15,10 +15,17 @@ import java.util.Map; * A {@link java.util.Map} providing additional atomic * <tt>putIfAbsent</tt>, <tt>remove</tt>, and <tt>replace</tt> methods. * + * <p>Memory consistency effects: As with other concurrent + * collections, actions in a thread prior to placing an object into a + * {@code ConcurrentMap} as a key or value + * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> + * actions subsequent to the access or removal of that object from + * the {@code ConcurrentMap} in another thread. + * * @since 1.5 * @author Doug Lea * @param <K> the type of keys maintained by this map - * @param <V> the type of mapped values + * @param <V> the type of mapped values */ public interface ConcurrentMap<K, V> extends Map<K, V> { /** @@ -26,93 +33,102 @@ public interface ConcurrentMap<K, V> extends Map<K, V> { * with a value, associate it with the given value. * This is equivalent to * <pre> - * if (!map.containsKey(key)) - * return map.put(key, value); + * if (!map.containsKey(key)) + * return map.put(key, value); * else - * return map.get(key); - * </pre> - * Except that the action is performed atomically. - * @param key key with which the specified value is to be associated. - * @param value value to be associated with the specified key. - * @return previous value associated with specified key, or <tt>null</tt> - * if there was no mapping for key. A <tt>null</tt> return can - * also indicate that the map previously associated <tt>null</tt> - * with the specified key, if the implementation supports - * <tt>null</tt> values. + * return map.get(key);</pre> + * except that the action is performed atomically. * - * @throws UnsupportedOperationException if the <tt>put</tt> operation is - * not supported by this map. + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previous value associated with the specified key, or + * <tt>null</tt> if there was no mapping for the key. + * (A <tt>null</tt> return can also indicate that the map + * previously associated <tt>null</tt> with the key, + * if the implementation supports null values.) + * @throws UnsupportedOperationException if the <tt>put</tt> operation + * is not supported by this map * @throws ClassCastException if the class of the specified key or value - * prevents it from being stored in this map. - * @throws IllegalArgumentException if some aspect of this key or value - * prevents it from being stored in this map. - * @throws NullPointerException if this map does not permit <tt>null</tt> - * keys or values, and the specified key or value is - * <tt>null</tt>. + * prevents it from being stored in this map + * @throws NullPointerException if the specified key or value is null, + * and this map does not permit null keys or values + * @throws IllegalArgumentException if some property of the specified key + * or value prevents it from being stored in this map * */ V putIfAbsent(K key, V value); /** - * Remove entry for key only if currently mapped to given value. - * Acts as - * <pre> - * if ((map.containsKey(key) && map.get(key).equals(value)) { - * map.remove(key); - * return true; - * } else return false; - * </pre> + * Removes the entry for a key only if currently mapped to a given value. + * This is equivalent to + * <pre> + * if (map.containsKey(key) && map.get(key).equals(value)) { + * map.remove(key); + * return true; + * } else return false;</pre> * except that the action is performed atomically. - * @param key key with which the specified value is associated. - * @param value value associated with the specified key. - * @return true if the value was removed, false otherwise - * @throws NullPointerException if this map does not permit <tt>null</tt> - * keys or values, and the specified key or value is - * <tt>null</tt>. + * + * @param key key with which the specified value is associated + * @param value value expected to be associated with the specified key + * @return <tt>true</tt> if the value was removed + * @throws UnsupportedOperationException if the <tt>remove</tt> operation + * is not supported by this map + * @throws ClassCastException if the key or value is of an inappropriate + * type for this map (optional) + * @throws NullPointerException if the specified key or value is null, + * and this map does not permit null keys or values (optional) */ boolean remove(Object key, Object value); - /** - * Replace entry for key only if currently mapped to given value. - * Acts as - * <pre> - * if ((map.containsKey(key) && map.get(key).equals(oldValue)) { - * map.put(key, newValue); - * return true; - * } else return false; - * </pre> + * Replaces the entry for a key only if currently mapped to a given value. + * This is equivalent to + * <pre> + * if (map.containsKey(key) && map.get(key).equals(oldValue)) { + * map.put(key, newValue); + * return true; + * } else return false;</pre> * except that the action is performed atomically. - * @param key key with which the specified value is associated. - * @param oldValue value expected to be associated with the specified key. - * @param newValue value to be associated with the specified key. - * @return true if the value was replaced - * @throws NullPointerException if this map does not permit <tt>null</tt> - * keys or values, and the specified key or value is - * <tt>null</tt>. + * + * @param key key with which the specified value is associated + * @param oldValue value expected to be associated with the specified key + * @param newValue value to be associated with the specified key + * @return <tt>true</tt> if the value was replaced + * @throws UnsupportedOperationException if the <tt>put</tt> operation + * is not supported by this map + * @throws ClassCastException if the class of a specified key or value + * prevents it from being stored in this map + * @throws NullPointerException if a specified key or value is null, + * and this map does not permit null keys or values + * @throws IllegalArgumentException if some property of a specified key + * or value prevents it from being stored in this map */ boolean replace(K key, V oldValue, V newValue); /** - * Replace entry for key only if currently mapped to some value. - * Acts as - * <pre> - * if ((map.containsKey(key)) { - * return map.put(key, value); - * } else return null; - * </pre> + * Replaces the entry for a key only if currently mapped to some value. + * This is equivalent to + * <pre> + * if (map.containsKey(key)) { + * return map.put(key, value); + * } else return null;</pre> * except that the action is performed atomically. - * @param key key with which the specified value is associated. - * @param value value to be associated with the specified key. - * @return previous value associated with specified key, or <tt>null</tt> - * if there was no mapping for key. A <tt>null</tt> return can - * also indicate that the map previously associated <tt>null</tt> - * with the specified key, if the implementation supports - * <tt>null</tt> values. - * @throws NullPointerException if this map does not permit <tt>null</tt> - * keys or values, and the specified key or value is - * <tt>null</tt>. + * + * @param key key with which the specified value is associated + * @param value value to be associated with the specified key + * @return the previous value associated with the specified key, or + * <tt>null</tt> if there was no mapping for the key. + * (A <tt>null</tt> return can also indicate that the map + * previously associated <tt>null</tt> with the key, + * if the implementation supports null values.) + * @throws UnsupportedOperationException if the <tt>put</tt> operation + * is not supported by this map + * @throws ClassCastException if the class of the specified key or value + * prevents it from being stored in this map + * @throws NullPointerException if the specified key or value is null, + * and this map does not permit null keys or values + * @throws IllegalArgumentException if some property of the specified key + * or value prevents it from being stored in this map */ V replace(K key, V value); - } diff --git a/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java b/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java index 7274595..f0c8ac6 100644 --- a/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java +++ b/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java @@ -1,1190 +1,1317 @@ /* - * 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. + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group. Adapted and released, under explicit permission, + * from JDK ArrayList.java which carries the following copyright: + * + * Copyright 1997 by Sun Microsystems, Inc., + * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. + * All rights reserved. + * + * This software is the confidential and proprietary information + * of Sun Microsystems, Inc. ("Confidential Information"). You + * shall not disclose such Confidential Information and shall use + * it only in accordance with the terms of the license agreement + * you entered into with Sun. */ package java.util.concurrent; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; +import java.util.*; +import java.util.concurrent.locks.*; import java.lang.reflect.Array; -import java.util.Collection; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.NoSuchElementException; -import java.util.RandomAccess; -import java.util.concurrent.locks.ReentrantLock; - -// BEGIN android-added + +import sun.misc.Unsafe; + +// BEGIN android-note +// removed link to collections framework docs +// END android-note + /** - * Implements a {@link java.util.ArrayList} variant that is thread-safe. All - * write operation result in a new copy of the underlying data being created. - * Iterators reflect the state of the CopyOnWriteArrayList at the time they were - * created. They are not updated to reflect subsequent changes to the list. In - * addition, these iterators cannot be used for modifying the underlying - * CopyOnWriteArrayList. - * - * @param <E> the element type + * A thread-safe variant of {@link java.util.ArrayList} in which all mutative + * operations (<tt>add</tt>, <tt>set</tt>, and so on) are implemented by + * making a fresh copy of the underlying array. + * + * <p> This is ordinarily too costly, but may be <em>more</em> efficient + * than alternatives when traversal operations vastly outnumber + * mutations, and is useful when you cannot or don't want to + * synchronize traversals, yet need to preclude interference among + * concurrent threads. The "snapshot" style iterator method uses a + * reference to the state of the array at the point that the iterator + * was created. This array never changes during the lifetime of the + * iterator, so interference is impossible and the iterator is + * guaranteed not to throw <tt>ConcurrentModificationException</tt>. + * The iterator will not reflect additions, removals, or changes to + * the list since the iterator was created. Element-changing + * operations on iterators themselves (<tt>remove</tt>, <tt>set</tt>, and + * <tt>add</tt>) are not supported. These methods throw + * <tt>UnsupportedOperationException</tt>. + * + * <p>All elements are permitted, including <tt>null</tt>. + * + * <p>Memory consistency effects: As with other concurrent + * collections, actions in a thread prior to placing an object into a + * {@code CopyOnWriteArrayList} + * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> + * actions subsequent to the access or removal of that element from + * the {@code CopyOnWriteArrayList} in another thread. + * + * @since 1.5 + * @author Doug Lea + * @param <E> the type of elements held in this collection */ -// END android-added -public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, Serializable { - +public class CopyOnWriteArrayList<E> + implements List<E>, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8673264195747942595L; - private transient volatile E[] arr; + /** The lock protecting all mutators */ + transient final ReentrantLock lock = new ReentrantLock(); - /** - * Lock for the queue write methods - */ - private final transient ReentrantLock lock = new ReentrantLock(); + /** The array, accessed only via getArray/setArray. */ + private volatile transient Object[] array; - // BEGIN android-added /** - * Creates a new, empty instance of CopyOnWriteArrayList. + * Gets the array. Non-private so as to also be accessible + * from CopyOnWriteArraySet class. */ - // END android-added - public CopyOnWriteArrayList() { + final Object[] getArray() { + return array; } - // BEGIN android-added /** - * Creates a new instance of CopyOnWriteArrayList and fills it with the - * contents of a given Collection. - * - * @param c the collection the elements of which are to be copied into - * the new instance. + * Sets the array. */ - // END android-added - public CopyOnWriteArrayList(Collection<? extends E> c) { - this((E[]) c.toArray()); + final void setArray(Object[] a) { + array = a; } - // BEGIN android-added /** - * Creates a new instance of CopyOnWriteArrayList and fills it with the - * contents of a given array. - * - * @param array the array the elements of which are to be copied into the - * new instance. + * Creates an empty list. */ - // END android-added - public CopyOnWriteArrayList(E[] array) { - int size = array.length; - E[] data = newElementArray(size); - for (int i = 0; i < size; i++) { - data[i] = array[i]; - } - arr = data; - } - - public boolean add(E e) { - lock.lock(); - try { - E[] data; - E[] old = getData(); - int size = old.length; - data = newElementArray(size + 1); - System.arraycopy(old, 0, data, 0, size); - data[size] = e; - setData(data); - return true; - } finally { - lock.unlock(); - } + public CopyOnWriteArrayList() { + setArray(new Object[0]); } - public void add(int index, E e) { - lock.lock(); - try { - E[] data; - E[] old = getData(); - int size = old.length; - checkIndexInclusive(index, size); - data = newElementArray(size+1); - System.arraycopy(old, 0, data, 0, index); - data[index] = e; - if (size > index) { - System.arraycopy(old, index, data, index + 1, size - index); - } - setData(data); - } finally { - lock.unlock(); - } + /** + * Creates a list containing the elements of the specified + * collection, in the order they are returned by the collection's + * iterator. + * + * @param c the collection of initially held elements + * @throws NullPointerException if the specified collection is null + */ + public CopyOnWriteArrayList(Collection<? extends E> c) { + Object[] elements = c.toArray(); + // c.toArray might (incorrectly) not return Object[] (see 6260652) + if (elements.getClass() != Object[].class) + elements = Java6Arrays.copyOf(elements, elements.length, Object[].class); + setArray(elements); } - public boolean addAll(Collection<? extends E> c) { - Iterator it = c.iterator(); - int ssize = c.size(); - lock.lock(); - try { - int size = size(); - E[] data; - E[] old = getData(); - int nSize = size + ssize; - data = newElementArray(nSize); - System.arraycopy(old, 0, data, 0, size); - while (it.hasNext()) { - data[size++] = (E) it.next(); - } - setData(data); - } finally { - lock.unlock(); - } - return true; + /** + * Creates a list holding a copy of the given array. + * + * @param toCopyIn the array (a copy of this array is used as the + * internal array) + * @throws NullPointerException if the specified array is null + */ + public CopyOnWriteArrayList(E[] toCopyIn) { + setArray(Java6Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class)); } - public boolean addAll(int index, Collection<? extends E> c) { - Iterator it = c.iterator(); - int ssize = c.size(); - lock.lock(); - try { - int size = size(); - checkIndexInclusive(index, size); - E[] data; - E[] old = getData(); - int nSize = size + ssize; - data = newElementArray(nSize); - System.arraycopy(old, 0, data, 0, index); - int i = index; - while (it.hasNext()) { - data[i++] = (E) it.next(); - } - if (size > index) { - System.arraycopy(old, index, data, index + ssize, size - index); - } - setData(data); - } finally { - lock.unlock(); - } - return true; + /** + * Returns the number of elements in this list. + * + * @return the number of elements in this list + */ + public int size() { + return getArray().length; } - // BEGIN android-added /** - * Adds to this CopyOnWriteArrayList all those elements from a given - * collection that are not yet part of the list. - * - * @param c the collection from which the potential new elements are - * taken. - * - * @return the number of elements actually added to this list. + * Returns <tt>true</tt> if this list contains no elements. + * + * @return <tt>true</tt> if this list contains no elements */ - // END android-added - public int addAllAbsent(Collection<? extends E> c) { - if (c.size() == 0) { - return 0; - } - lock.lock(); - try { - E[] old = getData(); - int size = old.length; - E[] toAdd = newElementArray(c.size()); - int i = 0; - for (Iterator it = c.iterator(); it.hasNext();) { - E o = (E) it.next(); - if (indexOf(o) < 0) { - toAdd[i++] = o; - } - } - E[] data = newElementArray(size + i); - System.arraycopy(old, 0, data, 0, size); - System.arraycopy(toAdd, 0, data, size, i); - setData(data); - return i; - } finally { - lock.unlock(); - } + public boolean isEmpty() { + return size() == 0; } - // BEGIN android-added /** - * Adds to this CopyOnWriteArrayList another element, given that this - * element is not yet part of the list. - * - * @param e the potential new element. - * - * @return true if the element was added, or false otherwise. + * Test for equality, coping with nulls. */ - // END android-added - public boolean addIfAbsent(E e) { - lock.lock(); - try { - E[] data; - E[] old = getData(); - int size = old.length; - if (size != 0) { - if (indexOf(e) >= 0) { - return false; - } - } - data = newElementArray(size + 1); - System.arraycopy(old, 0, data, 0, size); - data[size] = e; - setData(data); - return true; - } finally { - lock.unlock(); - } + private static boolean eq(Object o1, Object o2) { + return (o1 == null ? o2 == null : o1.equals(o2)); } - public void clear() { - lock.lock(); - try { - setData(newElementArray(0)); - } finally { - lock.unlock(); + /** + * static version of indexOf, to allow repeated calls without + * needing to re-acquire array each time. + * @param o element to search for + * @param elements the array + * @param index first index to search + * @param fence one past last index to search + * @return index of element, or -1 if absent + */ + private static int indexOf(Object o, Object[] elements, + int index, int fence) { + if (o == null) { + for (int i = index; i < fence; i++) + if (elements[i] == null) + return i; + } else { + for (int i = index; i < fence; i++) + if (o.equals(elements[i])) + return i; } + return -1; } - @Override - public Object clone() { - try { - CopyOnWriteArrayList thisClone = (CopyOnWriteArrayList) super.clone(); - thisClone.setData(this.getData()); - return thisClone; - } catch (CloneNotSupportedException e) { - throw new RuntimeException("CloneNotSupportedException is not expected here"); + /** + * static version of lastIndexOf. + * @param o element to search for + * @param elements the array + * @param index first index to search + * @return index of element, or -1 if absent + */ + private static int lastIndexOf(Object o, Object[] elements, int index) { + if (o == null) { + for (int i = index; i >= 0; i--) + if (elements[i] == null) + return i; + } else { + for (int i = index; i >= 0; i--) + if (o.equals(elements[i])) + return i; } + return -1; } + /** + * Returns <tt>true</tt> if this list contains the specified element. + * More formally, returns <tt>true</tt> if and only if this list contains + * at least one element <tt>e</tt> such that + * <tt>(o==null ? e==null : o.equals(e))</tt>. + * + * @param o element whose presence in this list is to be tested + * @return <tt>true</tt> if this list contains the specified element + */ public boolean contains(Object o) { - return indexOf(o) >= 0; - } - - public boolean containsAll(Collection<?> c) { - E[] data = getData(); - return containsAll(c, data, 0, data.length); - } - - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (!(o instanceof List)) { - return false; - } - List l = (List) o; - Iterator it = l.listIterator(); - Iterator ourIt = listIterator(); - while (it.hasNext()) { - if (!ourIt.hasNext()) { - return false; - } - Object thisListElem = it.next(); - Object anotherListElem = ourIt.next(); - if (!(thisListElem == null ? anotherListElem == null : thisListElem - .equals(anotherListElem))) { - return false; - } - } - if (ourIt.hasNext()) { - return false; - } - return true; + Object[] elements = getArray(); + return indexOf(o, elements, 0, elements.length) >= 0; } - public E get(int index) { - E[] data = getData(); - return data[index]; - } - - public int hashCode() { - int hashCode = 1; - Iterator it = listIterator(); - while (it.hasNext()) { - Object obj = it.next(); - hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode()); - } - return hashCode; + /** + * {@inheritDoc} + */ + public int indexOf(Object o) { + Object[] elements = getArray(); + return indexOf(o, elements, 0, elements.length); } - // BEGIN android-added /** - * Returns the index of a given element, starting the search from a given - * position in the list. - * - * @param e the element to search. - * @param index the index at which to start the search. - * - * @return the index of the element or null, if the element has not been - * found at or beyond the given start index. + * Returns the index of the first occurrence of the specified element in + * this list, searching forwards from <tt>index</tt>, or returns -1 if + * the element is not found. + * More formally, returns the lowest index <tt>i</tt> such that + * <tt>(i >= index && (e==null ? get(i)==null : e.equals(get(i))))</tt>, + * or -1 if there is no such index. + * + * @param e element to search for + * @param index index to start searching from + * @return the index of the first occurrence of the element in + * this list at position <tt>index</tt> or later in the list; + * <tt>-1</tt> if the element is not found. + * @throws IndexOutOfBoundsException if the specified index is negative */ - // END android-added public int indexOf(E e, int index) { - E[] data = getData(); - return indexOf(e, data, index, data.length - index); + Object[] elements = getArray(); + return indexOf(e, elements, index, elements.length); } - public int indexOf(Object o) { - E[] data = getData(); - return indexOf(o, data, 0, data.length); + /** + * {@inheritDoc} + */ + public int lastIndexOf(Object o) { + Object[] elements = getArray(); + return lastIndexOf(o, elements, elements.length - 1); } - public boolean isEmpty() { - return size() == 0; + /** + * Returns the index of the last occurrence of the specified element in + * this list, searching backwards from <tt>index</tt>, or returns -1 if + * the element is not found. + * More formally, returns the highest index <tt>i</tt> such that + * <tt>(i <= index && (e==null ? get(i)==null : e.equals(get(i))))</tt>, + * or -1 if there is no such index. + * + * @param e element to search for + * @param index index to start searching backwards from + * @return the index of the last occurrence of the element at position + * less than or equal to <tt>index</tt> in this list; + * -1 if the element is not found. + * @throws IndexOutOfBoundsException if the specified index is greater + * than or equal to the current size of this list + */ + public int lastIndexOf(E e, int index) { + Object[] elements = getArray(); + return lastIndexOf(e, elements, index); } - public Iterator<E> iterator() { - return new ListIteratorImpl(getData(), 0); + /** + * Returns a shallow copy of this list. (The elements themselves + * are not copied.) + * + * @return a clone of this list + */ + public Object clone() { + try { + CopyOnWriteArrayList c = (CopyOnWriteArrayList)(super.clone()); + c.resetLock(); + return c; + } catch (CloneNotSupportedException e) { + // this shouldn't happen, since we are Cloneable + throw new InternalError(); + } } - // BEGIN android-added /** - * Returns the last index of a given element, starting the search from - * a given position in the list and going backwards. - * - * @param e the element to search. - * @param index the index at which to start the search. - * - * @return the index of the element or null, if the element has not been - * found at or before the given start index. + * Returns an array containing all of the elements in this list + * in proper sequence (from first to last element). + * + * <p>The returned array will be "safe" in that no references to it are + * maintained by this list. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + * <p>This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all the elements in this list */ - // END android-added - public int lastIndexOf(E e, int index) { - E[] data = getData(); - return lastIndexOf(e, data, 0, index); + public Object[] toArray() { + Object[] elements = getArray(); + return Java6Arrays.copyOf(elements, elements.length); } - public int lastIndexOf(Object o) { - E[] data = getData(); - return lastIndexOf(o, data, 0, data.length); + /** + * Returns an array containing all of the elements in this list in + * proper sequence (from first to last element); the runtime type of + * the returned array is that of the specified array. If the list fits + * in the specified array, it is returned therein. Otherwise, a new + * array is allocated with the runtime type of the specified array and + * the size of this list. + * + * <p>If this list fits in the specified array with room to spare + * (i.e., the array has more elements than this list), the element in + * the array immediately following the end of the list is set to + * <tt>null</tt>. (This is useful in determining the length of this + * list <i>only</i> if the caller knows that this list does not contain + * any null elements.) + * + * <p>Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + * <p>Suppose <tt>x</tt> is a list known to contain only strings. + * The following code can be used to dump the list into a newly + * allocated array of <tt>String</tt>: + * + * <pre> + * String[] y = x.toArray(new String[0]);</pre> + * + * Note that <tt>toArray(new Object[0])</tt> is identical in function to + * <tt>toArray()</tt>. + * + * @param a the array into which the elements of the list are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose. + * @return an array containing all the elements in this list + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this list + * @throws NullPointerException if the specified array is null + */ + @SuppressWarnings("unchecked") + public <T> T[] toArray(T a[]) { + Object[] elements = getArray(); + int len = elements.length; + if (a.length < len) + return (T[]) Java6Arrays.copyOf(elements, len, a.getClass()); + else { + System.arraycopy(elements, 0, a, 0, len); + if (a.length > len) + a[len] = null; + return a; + } } - public ListIterator<E> listIterator() { - return new ListIteratorImpl(getData(), 0); - } + // Positional Access Operations - public ListIterator<E> listIterator(int index) { - E[] data = getData(); - checkIndexInclusive(index, data.length); - return new ListIteratorImpl(data, index); + @SuppressWarnings("unchecked") + private E get(Object[] a, int index) { + return (E) a[index]; } - public E remove(int index) { - return removeRange(index, 1); + /** + * {@inheritDoc} + * + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E get(int index) { + return get(getArray(), index); } - public boolean remove(Object o) { + /** + * Replaces the element at the specified position in this list with the + * specified element. + * + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E set(int index, E element) { + final ReentrantLock lock = this.lock; lock.lock(); try { - int index = indexOf(o); - if (index == -1) { - return false; + Object[] elements = getArray(); + E oldValue = get(elements, index); + + if (oldValue != element) { + int len = elements.length; + Object[] newElements = Java6Arrays.copyOf(elements, len); + newElements[index] = element; + setArray(newElements); + } else { + // Not quite a no-op; ensures volatile write semantics + setArray(elements); } - remove(index); - return true; + return oldValue; } finally { lock.unlock(); } } - public boolean removeAll(Collection<?> c) { + /** + * Appends the specified element to the end of this list. + * + * @param e element to be appended to this list + * @return <tt>true</tt> (as specified by {@link Collection#add}) + */ + public boolean add(E e) { + final ReentrantLock lock = this.lock; lock.lock(); try { - return removeAll(c, 0, getData().length) != 0; + Object[] elements = getArray(); + int len = elements.length; + Object[] newElements = Java6Arrays.copyOf(elements, len + 1); + newElements[len] = e; + setArray(newElements); + return true; } finally { lock.unlock(); } } - public boolean retainAll(Collection<?> c) { - if (c == null) { - throw new NullPointerException(); - } + /** + * Inserts the specified element at the specified position in this + * list. Shifts the element currently at that position (if any) and + * any subsequent elements to the right (adds one to their indices). + * + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public void add(int index, E element) { + final ReentrantLock lock = this.lock; lock.lock(); try { - return retainAll(c, 0, getData().length) != 0; + Object[] elements = getArray(); + int len = elements.length; + if (index > len || index < 0) + throw new IndexOutOfBoundsException("Index: "+index+ + ", Size: "+len); + Object[] newElements; + int numMoved = len - index; + if (numMoved == 0) + newElements = Java6Arrays.copyOf(elements, len + 1); + else { + newElements = new Object[len + 1]; + System.arraycopy(elements, 0, newElements, 0, index); + System.arraycopy(elements, index, newElements, index + 1, + numMoved); + } + newElements[index] = element; + setArray(newElements); } finally { lock.unlock(); } } - public E set(int index, E e) { + /** + * Removes the element at the specified position in this list. + * Shifts any subsequent elements to the left (subtracts one from their + * indices). Returns the element that was removed from the list. + * + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E remove(int index) { + final ReentrantLock lock = this.lock; lock.lock(); try { - int size = size(); - checkIndexExlusive(index, size); - E[] data; - data = newElementArray(size); - E[] oldArr = getData(); - System.arraycopy(oldArr, 0, data, 0, size); - E old = data[index]; - data[index] = e; - setData(data); - return old; + Object[] elements = getArray(); + int len = elements.length; + E oldValue = get(elements, index); + int numMoved = len - index - 1; + if (numMoved == 0) + setArray(Java6Arrays.copyOf(elements, len - 1)); + else { + Object[] newElements = new Object[len - 1]; + System.arraycopy(elements, 0, newElements, 0, index); + System.arraycopy(elements, index + 1, newElements, index, + numMoved); + setArray(newElements); + } + return oldValue; } finally { lock.unlock(); } } - public int size() { - return getData().length; - } - - public List<E> subList(int fromIndex, int toIndex) { - return new SubList(this, fromIndex, toIndex); - } - - public Object[] toArray() { - E[] data = getData(); - return toArray(data, 0, data.length); - } - - public <T> T[] toArray(T[] a) { - E[] data = getData(); - return (T[]) toArray(a, data, 0, data.length); - } - - @Override - public String toString() { - StringBuffer sb = new StringBuffer("["); + /** + * Removes the first occurrence of the specified element from this list, + * if it is present. If this list does not contain the element, it is + * unchanged. More formally, removes the element with the lowest index + * <tt>i</tt> such that + * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt> + * (if such an element exists). Returns <tt>true</tt> if this list + * contained the specified element (or equivalently, if this list + * changed as a result of the call). + * + * @param o element to be removed from this list, if present + * @return <tt>true</tt> if this list contained the specified element + */ + public boolean remove(Object o) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] elements = getArray(); + int len = elements.length; + if (len != 0) { + // Copy while searching for element to remove + // This wins in the normal case of element being present + int newlen = len - 1; + Object[] newElements = new Object[newlen]; + + for (int i = 0; i < newlen; ++i) { + if (eq(o, elements[i])) { + // found one; copy remaining and exit + for (int k = i + 1; k < len; ++k) + newElements[k-1] = elements[k]; + setArray(newElements); + return true; + } else + newElements[i] = elements[i]; + } - Iterator it = listIterator(); - while (it.hasNext()) { - sb.append(String.valueOf(it.next())); - sb.append(", "); - } - if (sb.length() > 1) { - sb.setLength(sb.length() - 2); + // special handling for last cell + if (eq(o, elements[newlen])) { + setArray(newElements); + return true; + } + } + return false; + } finally { + lock.unlock(); } - sb.append("]"); - return sb.toString(); } - // private and package private methods - - @SuppressWarnings("unchecked") - private final E[] newElementArray(int size) { - return (E[])new Object[size]; + /** + * Removes from this list all of the elements whose index is between + * <tt>fromIndex</tt>, inclusive, and <tt>toIndex</tt>, exclusive. + * Shifts any succeeding elements to the left (reduces their index). + * This call shortens the list by <tt>(toIndex - fromIndex)</tt> elements. + * (If <tt>toIndex==fromIndex</tt>, this operation has no effect.) + * + * @param fromIndex index of first element to be removed + * @param toIndex index after last element to be removed + * @throws IndexOutOfBoundsException if fromIndex or toIndex out of range + * (@code{fromIndex < 0 || toIndex > size() || toIndex < fromIndex}) + */ + private void removeRange(int fromIndex, int toIndex) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] elements = getArray(); + int len = elements.length; + + if (fromIndex < 0 || toIndex > len || toIndex < fromIndex) + throw new IndexOutOfBoundsException(); + int newlen = len - (toIndex - fromIndex); + int numMoved = len - toIndex; + if (numMoved == 0) + setArray(Java6Arrays.copyOf(elements, newlen)); + else { + Object[] newElements = new Object[newlen]; + System.arraycopy(elements, 0, newElements, 0, fromIndex); + System.arraycopy(elements, toIndex, newElements, + fromIndex, numMoved); + setArray(newElements); + } + } finally { + lock.unlock(); + } } /** - * sets the internal data array + * Append the element if not present. * - * @param data array to set + * @param e element to be added to this list, if absent + * @return <tt>true</tt> if the element was added */ - private final void setData(E[] data) { - arr = data; + public boolean addIfAbsent(E e) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + // Copy while checking if already present. + // This wins in the most common case where it is not present + Object[] elements = getArray(); + int len = elements.length; + Object[] newElements = new Object[len + 1]; + for (int i = 0; i < len; ++i) { + if (eq(e, elements[i])) + return false; // exit, throwing away copy + else + newElements[i] = elements[i]; + } + newElements[len] = e; + setArray(newElements); + return true; + } finally { + lock.unlock(); + } } /** - * gets the internal data array + * Returns <tt>true</tt> if this list contains all of the elements of the + * specified collection. * - * @return the data array + * @param c collection to be checked for containment in this list + * @return <tt>true</tt> if this list contains all of the elements of the + * specified collection + * @throws NullPointerException if the specified collection is null + * @see #contains(Object) */ - final E[] getData() { - if (arr == null) { - return newElementArray(0); + public boolean containsAll(Collection<?> c) { + Object[] elements = getArray(); + int len = elements.length; + for (Object e : c) { + if (indexOf(e, elements, 0, len) < 0) + return false; } - return arr; + return true; } /** - * Removes from the specified range of this list - * all the elements that are contained in the specified collection - * <p/> - * !should be called under lock + * Removes from this list all of its elements that are contained in + * the specified collection. This is a particularly expensive operation + * in this class because of the need for an internal temporary array. * - * @return Returns the number of removed elements + * @param c collection containing elements to be removed from this list + * @return <tt>true</tt> if this list changed as a result of the call + * @throws ClassCastException if the class of an element of this list + * is incompatible with the specified collection (optional) + * @throws NullPointerException if this list contains a null element and the + * specified collection does not permit null elements (optional), + * or if the specified collection is null + * @see #remove(Object) */ - final int removeAll(Collection c, int start, int size) { - int ssize = c.size(); - if (ssize == 0) { - return 0; - } - Object[] old = getData(); - int arrsize = old.length; - if (arrsize == 0) { - return 0; - } - Object[] data = new Object[size]; - int j = 0; - for (int i = start; i < (start + size); i++) { - if (!c.contains(old[i])) { - data[j++] = old[i]; + public boolean removeAll(Collection<?> c) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] elements = getArray(); + int len = elements.length; + if (len != 0) { + // temp array holds those elements we know we want to keep + int newlen = 0; + Object[] temp = new Object[len]; + for (int i = 0; i < len; ++i) { + Object element = elements[i]; + if (!c.contains(element)) + temp[newlen++] = element; + } + if (newlen != len) { + setArray(Java6Arrays.copyOf(temp, newlen)); + return true; + } } + return false; + } finally { + lock.unlock(); } - if (j != size) { - E[] result = newElementArray(arrsize - (size - j)); - System.arraycopy(old, 0, result, 0, start); - System.arraycopy(data, 0, result, start, j); - System.arraycopy(old, start + size, result, start + j, arrsize - - (start + size)); - setData(result); - return (size - j); - } - return 0; } /** - * Retains only the elements in the specified range of this list - * that are contained in the specified collection + * Retains only the elements in this list that are contained in the + * specified collection. In other words, removes from this list all of + * its elements that are not contained in the specified collection. * - * @return Returns the number of removed elements + * @param c collection containing elements to be retained in this list + * @return <tt>true</tt> if this list changed as a result of the call + * @throws ClassCastException if the class of an element of this list + * is incompatible with the specified collection (optional) + * @throws NullPointerException if this list contains a null element and the + * specified collection does not permit null elements (optional), + * or if the specified collection is null + * @see #remove(Object) */ - // should be called under lock - int retainAll(Collection c, int start, int size) { - Object[] old = getData(); - if (size == 0) { - return 0; - } - if (c.size() == 0) { - E[] data; - if (size == old.length) { - data = newElementArray(0); - } else { - data = newElementArray(old.length - size); - System.arraycopy(old, 0, data, 0, start); - System.arraycopy(old, start + size, data, start, old.length - - start - size); - } - setData(data); - return size; - } - Object[] temp = new Object[size]; - int pos = 0; - for (int i = start; i < (start + size); i++) { - if (c.contains(old[i])) { - temp[pos++] = old[i]; + public boolean retainAll(Collection<?> c) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] elements = getArray(); + int len = elements.length; + if (len != 0) { + // temp array holds those elements we know we want to keep + int newlen = 0; + Object[] temp = new Object[len]; + for (int i = 0; i < len; ++i) { + Object element = elements[i]; + if (c.contains(element)) + temp[newlen++] = element; + } + if (newlen != len) { + setArray(Java6Arrays.copyOf(temp, newlen)); + return true; + } } + return false; + } finally { + lock.unlock(); } - if (pos == size) { - return 0; - } - E[] data = newElementArray(pos + old.length - size); - System.arraycopy(old, 0, data, 0, start); - System.arraycopy(temp, 0, data, start, pos); - System.arraycopy(old, start + size, data, start + pos, old.length - - start - size); - setData(data); - return (size - pos); } /** - * Removes specified range from this list + * Appends all of the elements in the specified collection that + * are not already contained in this list, to the end of + * this list, in the order that they are returned by the + * specified collection's iterator. + * + * @param c collection containing elements to be added to this list + * @return the number of elements added + * @throws NullPointerException if the specified collection is null + * @see #addIfAbsent(Object) */ - E removeRange(int start, int size) { + public int addAllAbsent(Collection<? extends E> c) { + Object[] cs = c.toArray(); + if (cs.length == 0) + return 0; + Object[] uniq = new Object[cs.length]; + final ReentrantLock lock = this.lock; lock.lock(); try { - int sizeArr = size(); - checkIndexExlusive(start, sizeArr); - checkIndexInclusive(start + size, sizeArr); - E[] data; - data = newElementArray(sizeArr - size); - E[] oldArr = getData(); - System.arraycopy(oldArr, 0, data, 0, start); - E old = oldArr[start]; - if (sizeArr > (start + size)) { - System.arraycopy(oldArr, start + size, data, start, sizeArr - - (start + size)); + Object[] elements = getArray(); + int len = elements.length; + int added = 0; + for (int i = 0; i < cs.length; ++i) { // scan for duplicates + Object e = cs[i]; + if (indexOf(e, elements, 0, len) < 0 && + indexOf(e, uniq, 0, added) < 0) + uniq[added++] = e; } - setData(data); - return old; + if (added > 0) { + Object[] newElements = Java6Arrays.copyOf(elements, len + added); + System.arraycopy(uniq, 0, newElements, len, added); + setArray(newElements); + } + return added; } finally { lock.unlock(); } } - // some util static functions to use by iterators and methods /** - * Returns an array containing all of the elements - * in the specified range of the array in proper sequence + * Removes all of the elements from this list. + * The list will be empty after this call returns. */ - static Object[] toArray(Object[] data, int start, int size) { - Object[] result = new Object[size]; - System.arraycopy(data, start, result, 0, size); - return result; + public void clear() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + setArray(new Object[0]); + } finally { + lock.unlock(); + } } /** - * Returns an array containing all of the elements - * in the specified range of the array in proper sequence, - * stores the result in the array, specified by first parameter - * (as for public instance method toArray(Object[] to) + * Appends all of the elements in the specified collection to the end + * of this list, in the order that they are returned by the specified + * collection's iterator. + * + * @param c collection containing elements to be added to this list + * @return <tt>true</tt> if this list changed as a result of the call + * @throws NullPointerException if the specified collection is null + * @see #add(Object) */ - static Object[] toArray(Object[] to, Object[] data, int start, int size) { - int l = data.length; - if (to.length < l) { - to = (Object[]) Array.newInstance(to.getClass().getComponentType(), - l); - } else { - if (to.length > l) { - to[l] = null; - } + public boolean addAll(Collection<? extends E> c) { + Object[] cs = c.toArray(); + if (cs.length == 0) + return false; + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] elements = getArray(); + int len = elements.length; + Object[] newElements = Java6Arrays.copyOf(elements, len + cs.length); + System.arraycopy(cs, 0, newElements, len, cs.length); + setArray(newElements); + return true; + } finally { + lock.unlock(); } - System.arraycopy(data, start, to, 0, size); - return to; } /** - * Checks if the specified range of the - * array contains all of the elements in the collection + * Inserts all of the elements in the specified collection into this + * list, starting at the specified position. Shifts the element + * currently at that position (if any) and any subsequent elements to + * the right (increases their indices). The new elements will appear + * in this list in the order that they are returned by the + * specified collection's iterator. * - * @param c collection with elements - * @param data array where to search the elements - * @param start start index - * @param size size of the range + * @param index index at which to insert the first element + * from the specified collection + * @param c collection containing elements to be added to this list + * @return <tt>true</tt> if this list changed as a result of the call + * @throws IndexOutOfBoundsException {@inheritDoc} + * @throws NullPointerException if the specified collection is null + * @see #add(int,Object) */ - static final boolean containsAll(Collection c, Object[] data, int start, - int size) { - if (size == 0) { - return false; - } - Iterator it = c.iterator(); - while (it.hasNext()) { - Object next = it.next(); - if (indexOf(next, data, start, size) < 0) { + public boolean addAll(int index, Collection<? extends E> c) { + Object[] cs = c.toArray(); + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] elements = getArray(); + int len = elements.length; + if (index > len || index < 0) + throw new IndexOutOfBoundsException("Index: "+index+ + ", Size: "+len); + if (cs.length == 0) return false; + int numMoved = len - index; + Object[] newElements; + if (numMoved == 0) + newElements = Java6Arrays.copyOf(elements, len + cs.length); + else { + newElements = new Object[len + cs.length]; + System.arraycopy(elements, 0, newElements, 0, index); + System.arraycopy(elements, index, + newElements, index + cs.length, + numMoved); } + System.arraycopy(cs, 0, newElements, index, cs.length); + setArray(newElements); + return true; + } finally { + lock.unlock(); } - return true; } /** - * Returns the index in the specified range of the data array - * of the last occurrence of the specified element + * Save the state of the list to a stream (i.e., serialize it). * - * @param o element to search - * @param data array where to search - * @param start start index - * @param size size of the range - * @return + * @serialData The length of the array backing the list is emitted + * (int), followed by all of its elements (each an Object) + * in the proper order. + * @param s the stream */ - static final int lastIndexOf(Object o, Object[] data, int start, int size) { - if (size == 0) { - return -1; - } - if (o != null) { - for (int i = start + size - 1; i > start - 1; i--) { - if (o.equals(data[i])) { - return i; - } - } - } else { - for (int i = start + size - 1; i > start - 1; i--) { - if (data[i] == null) { - return i; - } - } - } - return -1; + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException{ + + // Write out element count, and any hidden stuff + s.defaultWriteObject(); + + Object[] elements = getArray(); + int len = elements.length; + // Write out array length + s.writeInt(len); + + // Write out all elements in the proper order. + for (int i = 0; i < len; i++) + s.writeObject(elements[i]); } /** - * Returns the index in the specified range of the data array - * of the first occurrence of the specified element + * Reconstitute the list from a stream (i.e., deserialize it). + * @param s the stream + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + + // Read in size, and any hidden stuff + s.defaultReadObject(); + + // bind to new lock + resetLock(); + + // Read in array length and allocate array + int len = s.readInt(); + Object[] elements = new Object[len]; + + // Read in all elements in the proper order. + for (int i = 0; i < len; i++) + elements[i] = s.readObject(); + setArray(elements); + } + + /** + * Returns a string representation of this list. The string + * representation consists of the string representations of the list's + * elements in the order they are returned by its iterator, enclosed in + * square brackets (<tt>"[]"</tt>). Adjacent elements are separated by + * the characters <tt>", "</tt> (comma and space). Elements are + * converted to strings as by {@link String#valueOf(Object)}. * - * @param o element to search - * @param data array where to search - * @param start start index - * @param size end index - * @return + * @return a string representation of this list */ - static final int indexOf(Object o, Object[] data, int start, int size) { - if (size == 0) { - return -1; - } - if (o == null) { - for (int i = start; i < start + size; i++) { - if (data[i] == null) { - return i; - } - } - } else { - for (int i = start; i < start + size; i++) { - if (o.equals(data[i])) { - return i; - } - } - } - return -1; + public String toString() { + return Arrays.toString(getArray()); } /** - * Throws <code>IndexOutOfBoundsException</code> if <code>index</code> - * is out of the list bounds. + * Compares the specified object with this list for equality. + * Returns {@code true} if the specified object is the same object + * as this object, or if it is also a {@link List} and the sequence + * of elements returned by an {@linkplain List#iterator() iterator} + * over the specified list is the same as the sequence returned by + * an iterator over this list. The two sequences are considered to + * be the same if they have the same length and corresponding + * elements at the same position in the sequence are <em>equal</em>. + * Two elements {@code e1} and {@code e2} are considered + * <em>equal</em> if {@code (e1==null ? e2==null : e1.equals(e2))}. * - * @param index element index to check. + * @param o the object to be compared for equality with this list + * @return {@code true} if the specified object is equal to this list */ - static final void checkIndexInclusive(int index, int size) { - if (index < 0 || index > size) { - throw new IndexOutOfBoundsException("Index is " + index + ", size is " + size); - } + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof List)) + return false; + + List<?> list = (List<?>)(o); + Iterator<?> it = list.iterator(); + Object[] elements = getArray(); + int len = elements.length; + for (int i = 0; i < len; ++i) + if (!it.hasNext() || !eq(elements[i], it.next())) + return false; + if (it.hasNext()) + return false; + return true; } /** - * Throws <code>IndexOutOfBoundsException</code> if <code>index</code> - * is out of the list bounds. Excluding the last element. + * Returns the hash code value for this list. + * + * <p>This implementation uses the definition in {@link List#hashCode}. * - * @param index element index to check. + * @return the hash code value for this list */ - static final void checkIndexExlusive(int index, int size) { - if (index < 0 || index >= size) { - throw new IndexOutOfBoundsException("Index is " + index + ", size is " + size); + public int hashCode() { + int hashCode = 1; + Object[] elements = getArray(); + int len = elements.length; + for (int i = 0; i < len; ++i) { + Object obj = elements[i]; + hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode()); } + return hashCode; } /** - * list iterator implementation, - * when created gets snapshot of the list, - * so never throws ConcurrentModificationException + * Returns an iterator over the elements in this list in proper sequence. + * + * <p>The returned iterator provides a snapshot of the state of the list + * when the iterator was constructed. No synchronization is needed while + * traversing the iterator. The iterator does <em>NOT</em> support the + * <tt>remove</tt> method. + * + * @return an iterator over the elements in this list in proper sequence */ - private static class ListIteratorImpl implements ListIterator { - private final Object[] arr; + public Iterator<E> iterator() { + return new COWIterator<E>(getArray(), 0); + } - private int current; + /** + * {@inheritDoc} + * + * <p>The returned iterator provides a snapshot of the state of the list + * when the iterator was constructed. No synchronization is needed while + * traversing the iterator. The iterator does <em>NOT</em> support the + * <tt>remove</tt>, <tt>set</tt> or <tt>add</tt> methods. + */ + public ListIterator<E> listIterator() { + return new COWIterator<E>(getArray(), 0); + } - private final int size; + /** + * {@inheritDoc} + * + * <p>The returned iterator provides a snapshot of the state of the list + * when the iterator was constructed. No synchronization is needed while + * traversing the iterator. The iterator does <em>NOT</em> support the + * <tt>remove</tt>, <tt>set</tt> or <tt>add</tt> methods. + * + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public ListIterator<E> listIterator(final int index) { + Object[] elements = getArray(); + int len = elements.length; + if (index<0 || index>len) + throw new IndexOutOfBoundsException("Index: "+index); - final int size() { - return size; - } + return new COWIterator<E>(elements, index); + } - public ListIteratorImpl(Object[] data, int current) { - this.current = current; - arr = data; - size = data.length; - } + private static class COWIterator<E> implements ListIterator<E> { + /** Snapshot of the array **/ + private final Object[] snapshot; + /** Index of element to be returned by subsequent call to next. */ + private int cursor; - public void add(Object o) { - throw new UnsupportedOperationException("Unsupported operation add"); + private COWIterator(Object[] elements, int initialCursor) { + cursor = initialCursor; + snapshot = elements; } public boolean hasNext() { - if (current < size) { - return true; - } - return false; + return cursor < snapshot.length; } public boolean hasPrevious() { - return current > 0; + return cursor > 0; } - public Object next() { - if (hasNext()) { - return arr[current++]; - } - throw new NoSuchElementException("pos is " + current + ", size is " + size); + @SuppressWarnings("unchecked") + public E next() { + if (! hasNext()) + throw new NoSuchElementException(); + return (E) snapshot[cursor++]; } - public int nextIndex() { - return current; + @SuppressWarnings("unchecked") + public E previous() { + if (! hasPrevious()) + throw new NoSuchElementException(); + return (E) snapshot[--cursor]; } - public Object previous() { - if (hasPrevious()) { - return arr[--current]; - } - throw new NoSuchElementException("pos is " + (current-1) + ", size is " + size); + public int nextIndex() { + return cursor; } public int previousIndex() { - return current - 1; + return cursor-1; } + /** + * Not supported. Always throws UnsupportedOperationException. + * @throws UnsupportedOperationException always; <tt>remove</tt> + * is not supported by this iterator. + */ public void remove() { - throw new UnsupportedOperationException("Unsupported operation remove"); + throw new UnsupportedOperationException(); } - public void set(Object o) { - throw new UnsupportedOperationException("Unsupported operation set"); + /** + * Not supported. Always throws UnsupportedOperationException. + * @throws UnsupportedOperationException always; <tt>set</tt> + * is not supported by this iterator. + */ + public void set(E e) { + throw new UnsupportedOperationException(); } + /** + * Not supported. Always throws UnsupportedOperationException. + * @throws UnsupportedOperationException always; <tt>add</tt> + * is not supported by this iterator. + */ + public void add(E e) { + throw new UnsupportedOperationException(); + } } /** - * Keeps a state of sublist implementation, - * size and array declared as final, - * so we'll never get the unconsistent state + * Returns a view of the portion of this list between + * <tt>fromIndex</tt>, inclusive, and <tt>toIndex</tt>, exclusive. + * The returned list is backed by this list, so changes in the + * returned list are reflected in this list. + * + * <p>The semantics of the list returned by this method become + * undefined if the backing list (i.e., this list) is modified in + * any way other than via the returned list. + * + * @param fromIndex low endpoint (inclusive) of the subList + * @param toIndex high endpoint (exclusive) of the subList + * @return a view of the specified range within this list + * @throws IndexOutOfBoundsException {@inheritDoc} */ - static final class SubListReadData { - final int size; - - final Object[] data; - - SubListReadData(int size, Object[] data) { - this.size = size; - this.data = data; + public List<E> subList(int fromIndex, int toIndex) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] elements = getArray(); + int len = elements.length; + if (fromIndex < 0 || toIndex > len || fromIndex > toIndex) + throw new IndexOutOfBoundsException(); + return new COWSubList<E>(this, fromIndex, toIndex); + } finally { + lock.unlock(); } } /** - * Represents a list returned by <code>sublist()</code>. + * Sublist for CopyOnWriteArrayList. + * This class extends AbstractList merely for convenience, to + * avoid having to define addAll, etc. This doesn't hurt, but + * is wasteful. This class does not need or use modCount + * mechanics in AbstractList, but does need to check for + * concurrent modification using similar mechanics. On each + * operation, the array that we expect the backing list to use + * is checked and updated. Since we do this for all of the + * base operations invoked by those defined in AbstractList, + * all is well. While inefficient, this is not worth + * improving. The kinds of list operations inherited from + * AbstractList are already so slow on COW sublists that + * adding a bit more space/time doesn't seem even noticeable. */ - static class SubList implements List { - private final CopyOnWriteArrayList list; - - private volatile SubListReadData read; - - private final int start; - - /** - * Sublist constructor. - * - * @param list backing list. - * @param fromIdx startingIndex, inclusive - * @param toIdx endIndex, exclusive - */ - public SubList(CopyOnWriteArrayList list, int fromIdx, int toIdx) { - this.list = list; - Object[] data = list.getData(); - int size = toIdx - fromIdx; - checkIndexExlusive(fromIdx, data.length); - checkIndexInclusive(toIdx, data.length); - read = new SubListReadData(size, list.getData()); - start = fromIdx; - } - - /** - * throws ConcurrentModificationException when the list - * is structurally modified in the other way other than via this subList - * <p/> - * Should be called under lock! - */ - private void checkModifications() { - if (read.data != list.getData()) { + private static class COWSubList<E> + extends AbstractList<E> + implements RandomAccess + { + private final CopyOnWriteArrayList<E> l; + private final int offset; + private int size; + private Object[] expectedArray; + + // only call this holding l's lock + COWSubList(CopyOnWriteArrayList<E> list, + int fromIndex, int toIndex) { + l = list; + expectedArray = l.getArray(); + offset = fromIndex; + size = toIndex - fromIndex; + } + + // only call this holding l's lock + private void checkForComodification() { + if (l.getArray() != expectedArray) throw new ConcurrentModificationException(); - } } - /** - * @see java.util.List#listIterator(int) - */ - public ListIterator listIterator(int startIdx) { - return new SubListIterator(startIdx, read); + // only call this holding l's lock + private void rangeCheck(int index) { + if (index<0 || index>=size) + throw new IndexOutOfBoundsException("Index: "+index+ + ",Size: "+size); } - /** - * @see java.util.List#set(int, java.lang.Object) - */ - public Object set(int index, Object obj) { - list.lock.lock(); + public E set(int index, E element) { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkIndexExlusive(index, read.size); - checkModifications(); - Object result = list.set(index + start, obj); - read = new SubListReadData(read.size, list.getData()); - return result; + rangeCheck(index); + checkForComodification(); + E x = l.set(index+offset, element); + expectedArray = l.getArray(); + return x; } finally { - list.lock.unlock(); + lock.unlock(); } } - /** - * @see java.util.List#get(int) - */ - public Object get(int index) { - SubListReadData data = read; - if (data.data != list.getData()) { - list.lock.lock(); - try { - data = read; - if (data.data != list.getData()) { - throw new ConcurrentModificationException(); - } - } finally { - list.lock.unlock(); - } - } - checkIndexExlusive(index, data.size); - return data.data[index + start]; - } - - /** - * @see java.util.Collection#size() - */ - public int size() { - return read.size; - } - - /** - * @see java.util.List#remove(int) - */ - public Object remove(int index) { - list.lock.lock(); + public E get(int index) { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkIndexExlusive(index, read.size); - checkModifications(); - Object obj = list.remove(index + start); - read = new SubListReadData(read.size - 1, list.getData()); - return obj; + rangeCheck(index); + checkForComodification(); + return l.get(index+offset); } finally { - list.lock.unlock(); + lock.unlock(); } } - /** - * @see java.util.List#add(int, java.lang.Object) - */ - public void add(int index, Object object) { - list.lock.lock(); + public int size() { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkIndexInclusive(index, read.size); - checkModifications(); - list.add(index + start, object); - read = new SubListReadData(read.size + 1, list.getData()); + checkForComodification(); + return size; } finally { - list.lock.unlock(); + lock.unlock(); } } - public boolean add(Object o) { - list.lock.lock(); + public void add(int index, E element) { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkModifications(); - list.add(start + read.size, o); - read = new SubListReadData(read.size + 1, list.getData()); - return true; + checkForComodification(); + if (index<0 || index>size) + throw new IndexOutOfBoundsException(); + l.add(index+offset, element); + expectedArray = l.getArray(); + size++; } finally { - list.lock.unlock(); + lock.unlock(); } } - public boolean addAll(Collection c) { - list.lock.lock(); + public void clear() { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkModifications(); - int d = list.size(); - list.addAll(start + read.size, c); - read = new SubListReadData(read.size + (list.size() - d), list - .getData()); - return true; + checkForComodification(); + l.removeRange(offset, offset+size); + expectedArray = l.getArray(); + size = 0; } finally { - list.lock.unlock(); + lock.unlock(); } } - public void clear() { - list.lock.lock(); + public E remove(int index) { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkModifications(); - list.removeRange(start, read.size); - read = new SubListReadData(0, list.getData()); + rangeCheck(index); + checkForComodification(); + E result = l.remove(index+offset); + expectedArray = l.getArray(); + size--; + return result; } finally { - list.lock.unlock(); + lock.unlock(); } } - public boolean contains(Object o) { - return indexOf(o) != -1; - } - - public boolean containsAll(Collection c) { - SubListReadData b = read; - return CopyOnWriteArrayList.containsAll(c, b.data, start, b.size); - } - - public int indexOf(Object o) { - SubListReadData b = read; - int ind = CopyOnWriteArrayList.indexOf(o, b.data, start, b.size) - - start; - return ind < 0 ? -1 : ind; - } - - public boolean isEmpty() { - return read.size == 0; - } - - public Iterator iterator() { - return new SubListIterator(0, read); - } - - public int lastIndexOf(Object o) { - SubListReadData b = read; - int ind = CopyOnWriteArrayList - .lastIndexOf(o, b.data, start, b.size) - - start; - return ind < 0 ? -1 : ind; - } - - public ListIterator listIterator() { - return new SubListIterator(0, read); + public boolean remove(Object o) { + int index = indexOf(o); + if (index == -1) + return false; + remove(index); + return true; } - public boolean remove(Object o) { - list.lock.lock(); + public Iterator<E> iterator() { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkModifications(); - int i = indexOf(o); - if (i == -1) { - return false; - } - boolean result = list.remove(i + start) != null; - if (result) { - read = new SubListReadData(read.size - 1, list.getData()); - } - return result; + checkForComodification(); + return new COWSubListIterator<E>(l, 0, offset, size); } finally { - list.lock.unlock(); + lock.unlock(); } } - public boolean removeAll(Collection c) { - list.lock.lock(); + public ListIterator<E> listIterator(final int index) { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkModifications(); - int removed = list.removeAll(c, start, read.size); - if (removed > 0) { - read = new SubListReadData(read.size - removed, list - .getData()); - return true; - } + checkForComodification(); + if (index<0 || index>size) + throw new IndexOutOfBoundsException("Index: "+index+ + ", Size: "+size); + return new COWSubListIterator<E>(l, index, offset, size); } finally { - list.lock.unlock(); + lock.unlock(); } - return false; } - public boolean retainAll(Collection c) { - list.lock.lock(); + public List<E> subList(int fromIndex, int toIndex) { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkModifications(); - int removed = list.retainAll(c, start, read.size); - if (removed > 0) { - read = new SubListReadData(read.size - removed, list - .getData()); - return true; - } - return false; + checkForComodification(); + if (fromIndex<0 || toIndex>size) + throw new IndexOutOfBoundsException(); + return new COWSubList<E>(l, fromIndex + offset, + toIndex + offset); } finally { - list.lock.unlock(); + lock.unlock(); } } - public List subList(int fromIndex, int toIndex) { - return new SubList(list, start + fromIndex, start + toIndex); - } + } + - public Object[] toArray() { - SubListReadData r = read; - return CopyOnWriteArrayList.toArray(r.data, start, r.size); + private static class COWSubListIterator<E> implements ListIterator<E> { + private final ListIterator<E> i; + private final int index; + private final int offset; + private final int size; + + COWSubListIterator(List<E> l, int index, int offset, + int size) { + this.index = index; + this.offset = offset; + this.size = size; + i = l.listIterator(index+offset); } - public Object[] toArray(Object[] a) { - SubListReadData r = read; - return CopyOnWriteArrayList.toArray(a, r.data, start, r.size); + public boolean hasNext() { + return nextIndex() < size; } - /** - * @see java.util.List#addAll(int, java.util.Collection) - */ - public boolean addAll(int index, Collection collection) { - list.lock.lock(); - try { - checkIndexInclusive(index, read.size); - checkModifications(); - int d = list.size(); - boolean rt = list.addAll(index + start, collection); - read = new SubListReadData(read.size + list.size() - d, list - .getData()); - return rt; - } finally { - list.lock.unlock(); - } + public E next() { + if (hasNext()) + return i.next(); + else + throw new NoSuchElementException(); } - /** - * Implementation of <code>ListIterator</code> for the - * <code>SubList</code> - * gets a snapshot of the sublist, - * never throws ConcurrentModificationException - */ - private class SubListIterator extends ListIteratorImpl { - private final SubListReadData dataR; - - /** - * Constructs an iterator starting with the given index - * - * @param index index of the first element to iterate. - */ - private SubListIterator(int index, SubListReadData d) { - super(d.data, index + start); - this.dataR = d; - } + public boolean hasPrevious() { + return previousIndex() >= 0; + } - /** - * @see java.util.ListIterator#nextIndex() - */ - public int nextIndex() { - return super.nextIndex() - start; - } + public E previous() { + if (hasPrevious()) + return i.previous(); + else + throw new NoSuchElementException(); + } - /** - * @see java.util.ListIterator#previousIndex() - */ - public int previousIndex() { - return super.previousIndex() - start; - } + public int nextIndex() { + return i.nextIndex() - offset; + } - /** - * @see java.util.Iterator#hasNext() - */ - public boolean hasNext() { - return nextIndex() < dataR.size; - } + public int previousIndex() { + return i.previousIndex() - offset; + } - /** - * @see java.util.ListIterator#hasPrevious() - */ - public boolean hasPrevious() { - return previousIndex() > -1; - } + public void remove() { + throw new UnsupportedOperationException(); } - } + public void set(E e) { + throw new UnsupportedOperationException(); + } - //serialization support - /** - * Writes the object state to the ObjectOutputStream. - * - * @param oos ObjectOutputStream to write object to. - * @throws IOException if an I/O error occur. - */ - private void writeObject(ObjectOutputStream oos) throws IOException { - E[] back = getData(); - int size = back.length; - oos.defaultWriteObject(); - oos.writeInt(size); - for (int i = 0; i < size; i++) { - oos.writeObject(back[i]); + public void add(E e) { + throw new UnsupportedOperationException(); } } - /** - * Reads the object state from the ObjectOutputStream. - * - * @param ois ObjectInputStream to read object from. - * @throws IOException if an I/O error occur. - */ - private void readObject(ObjectInputStream ois) throws IOException, - ClassNotFoundException { - ois.defaultReadObject(); - int length = ois.readInt(); - if (length == 0) { - setData(newElementArray(0)); - } else { - E[] back = newElementArray(length); - for (int i = 0; i < back.length; i++) { - back[i] = (E) ois.readObject(); - } - setData(back); - } + // Support for resetting lock while deserializing + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final long lockOffset; + static { + try { + lockOffset = unsafe.objectFieldOffset + (CopyOnWriteArrayList.class.getDeclaredField("lock")); + } catch (Exception ex) { throw new Error(ex); } + } + private void resetLock() { + unsafe.putObjectVolatile(this, lockOffset, new ReentrantLock()); } } diff --git a/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java b/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java index 532952b..65a05de 100644 --- a/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java +++ b/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain. Use, modify, and - * redistribute this code in any way without acknowledgement. + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/licenses/publicdomain */ package java.util.concurrent; @@ -9,39 +9,37 @@ import java.util.*; // BEGIN android-note // removed link to collections framework docs -// Removed clonable interface to be closer to the RI. // END android-note /** - * A {@link java.util.Set} that uses {@link - * java.util.concurrent.CopyOnWriteArrayList} for all of its - * operations. Thus, it shares the same basic properties: + * A {@link java.util.Set} that uses an internal {@link CopyOnWriteArrayList} + * for all of its operations. Thus, it shares the same basic properties: * <ul> * <li>It is best suited for applications in which set sizes generally * stay small, read-only operations * vastly outnumber mutative operations, and you need * to prevent interference among threads during traversal. - * <li>Mutative operations(add, set, remove, etc) are expensive - * since they usually entail copying the entire underlying array. - * <li>Iterators do not support the mutative remove operation - * <li>Traversal via iterators is very fast and cannot ever encounter + * <li>It is thread-safe. + * <li>Mutative operations (<tt>add</tt>, <tt>set</tt>, <tt>remove</tt>, etc.) + * are expensive since they usually entail copying the entire underlying + * array. + * <li>Iterators do not support the mutative <tt>remove</tt> operation. + * <li>Traversal via iterators is fast and cannot encounter * interference from other threads. Iterators rely on * unchanging snapshots of the array at the time the iterators were - * constructed. + * constructed. * </ul> - * <p> - * <b>Sample Usage.</b> Probably the main application - * of copy-on-write sets are classes that maintain - * sets of Handler objects - * that must be multicasted to upon an update command. This - * is a classic case where you do not want to be holding a - * lock while sending a message, and where traversals normally - * vastly overwhelm additions. + * + * <p> <b>Sample Usage.</b> The following code sketch uses a + * copy-on-write set to maintain a set of Handler objects that + * perform some action upon state updates. + * * <pre> * class Handler { void handle(); ... } * * class X { - * private final CopyOnWriteArraySet<Handler> handlers = new CopyOnWriteArraySet<Handler>(); + * private final CopyOnWriteArraySet<Handler> handlers + * = new CopyOnWriteArraySet<Handler>(); * public void addHandler(Handler h) { handlers.add(h); } * * private long internalState; @@ -49,14 +47,13 @@ import java.util.*; * * public void update() { * changeState(); - * Iterator it = handlers.iterator(); - * while (it.hasNext()) - * it.next().handle(); + * for (Handler handler : handlers) + * handler.handle(); * } * } * </pre> - * @see CopyOnWriteArrayList * + * @see CopyOnWriteArrayList * @since 1.5 * @author Doug Lea * @param <E> the type of elements held in this collection @@ -76,27 +73,292 @@ public class CopyOnWriteArraySet<E> extends AbstractSet<E> /** * Creates a set containing all of the elements of the specified - * Collection. - * @param c the collection + * collection. + * + * @param c the collection of elements to initially contain + * @throws NullPointerException if the specified collection is null */ public CopyOnWriteArraySet(Collection<? extends E> c) { al = new CopyOnWriteArrayList<E>(); al.addAllAbsent(c); } + /** + * Returns the number of elements in this set. + * + * @return the number of elements in this set + */ + public int size() { + return al.size(); + } + + /** + * Returns <tt>true</tt> if this set contains no elements. + * + * @return <tt>true</tt> if this set contains no elements + */ + public boolean isEmpty() { + return al.isEmpty(); + } + + /** + * Returns <tt>true</tt> if this set contains the specified element. + * More formally, returns <tt>true</tt> if and only if this set + * contains an element <tt>e</tt> such that + * <tt>(o==null ? e==null : o.equals(e))</tt>. + * + * @param o element whose presence in this set is to be tested + * @return <tt>true</tt> if this set contains the specified element + */ + public boolean contains(Object o) { + return al.contains(o); + } + + /** + * Returns an array containing all of the elements in this set. + * If this set makes any guarantees as to what order its elements + * are returned by its iterator, this method must return the + * elements in the same order. + * + * <p>The returned array will be "safe" in that no references to it + * are maintained by this set. (In other words, this method must + * allocate a new array even if this set is backed by an array). + * The caller is thus free to modify the returned array. + * + * <p>This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all the elements in this set + */ + public Object[] toArray() { + return al.toArray(); + } + + /** + * Returns an array containing all of the elements in this set; the + * runtime type of the returned array is that of the specified array. + * If the set fits in the specified array, it is returned therein. + * Otherwise, a new array is allocated with the runtime type of the + * specified array and the size of this set. + * + * <p>If this set fits in the specified array with room to spare + * (i.e., the array has more elements than this set), the element in + * the array immediately following the end of the set is set to + * <tt>null</tt>. (This is useful in determining the length of this + * set <i>only</i> if the caller knows that this set does not contain + * any null elements.) + * + * <p>If this set makes any guarantees as to what order its elements + * are returned by its iterator, this method must return the elements + * in the same order. + * + * <p>Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + * <p>Suppose <tt>x</tt> is a set known to contain only strings. + * The following code can be used to dump the set into a newly allocated + * array of <tt>String</tt>: + * + * <pre> + * String[] y = x.toArray(new String[0]);</pre> + * + * Note that <tt>toArray(new Object[0])</tt> is identical in function to + * <tt>toArray()</tt>. + * + * @param a the array into which the elements of this set are to be + * stored, if it is big enough; otherwise, a new array of the same + * runtime type is allocated for this purpose. + * @return an array containing all the elements in this set + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in this + * set + * @throws NullPointerException if the specified array is null + */ + public <T> T[] toArray(T[] a) { + return al.toArray(a); + } + + /** + * Removes all of the elements from this set. + * The set will be empty after this call returns. + */ + public void clear() { + al.clear(); + } - public int size() { return al.size(); } - public boolean isEmpty() { return al.isEmpty(); } - public boolean contains(Object o) { return al.contains(o); } - public Object[] toArray() { return al.toArray(); } - public <T> T[] toArray(T[] a) { return al.toArray(a); } - public void clear() { al.clear(); } - public Iterator<E> iterator() { return al.iterator(); } - public boolean remove(Object o) { return al.remove(o); } - public boolean add(E o) { return al.addIfAbsent(o); } - public boolean containsAll(Collection<?> c) { return al.containsAll(c); } - public boolean addAll(Collection<? extends E> c) { return al.addAllAbsent(c) > 0; } - public boolean removeAll(Collection<?> c) { return al.removeAll(c); } - public boolean retainAll(Collection<?> c) { return al.retainAll(c); } + /** + * Removes the specified element from this set if it is present. + * More formally, removes an element <tt>e</tt> such that + * <tt>(o==null ? e==null : o.equals(e))</tt>, + * if this set contains such an element. Returns <tt>true</tt> if + * this set contained the element (or equivalently, if this set + * changed as a result of the call). (This set will not contain the + * element once the call returns.) + * + * @param o object to be removed from this set, if present + * @return <tt>true</tt> if this set contained the specified element + */ + public boolean remove(Object o) { + return al.remove(o); + } + /** + * Adds the specified element to this set if it is not already present. + * More formally, adds the specified element <tt>e</tt> to this set if + * the set contains no element <tt>e2</tt> such that + * <tt>(e==null ? e2==null : e.equals(e2))</tt>. + * If this set already contains the element, the call leaves the set + * unchanged and returns <tt>false</tt>. + * + * @param e element to be added to this set + * @return <tt>true</tt> if this set did not already contain the specified + * element + */ + public boolean add(E e) { + return al.addIfAbsent(e); + } + + /** + * Returns <tt>true</tt> if this set contains all of the elements of the + * specified collection. If the specified collection is also a set, this + * method returns <tt>true</tt> if it is a <i>subset</i> of this set. + * + * @param c collection to be checked for containment in this set + * @return <tt>true</tt> if this set contains all of the elements of the + * specified collection + * @throws NullPointerException if the specified collection is null + * @see #contains(Object) + */ + public boolean containsAll(Collection<?> c) { + return al.containsAll(c); + } + + /** + * Adds all of the elements in the specified collection to this set if + * they're not already present. If the specified collection is also a + * set, the <tt>addAll</tt> operation effectively modifies this set so + * that its value is the <i>union</i> of the two sets. The behavior of + * this operation is undefined if the specified collection is modified + * while the operation is in progress. + * + * @param c collection containing elements to be added to this set + * @return <tt>true</tt> if this set changed as a result of the call + * @throws NullPointerException if the specified collection is null + * @see #add(Object) + */ + public boolean addAll(Collection<? extends E> c) { + return al.addAllAbsent(c) > 0; + } + + /** + * Removes from this set all of its elements that are contained in the + * specified collection. If the specified collection is also a set, + * this operation effectively modifies this set so that its value is the + * <i>asymmetric set difference</i> of the two sets. + * + * @param c collection containing elements to be removed from this set + * @return <tt>true</tt> if this set changed as a result of the call + * @throws ClassCastException if the class of an element of this set + * is incompatible with the specified collection (optional) + * @throws NullPointerException if this set contains a null element and the + * specified collection does not permit null elements (optional), + * or if the specified collection is null + * @see #remove(Object) + */ + public boolean removeAll(Collection<?> c) { + return al.removeAll(c); + } + + /** + * Retains only the elements in this set that are contained in the + * specified collection. In other words, removes from this set all of + * its elements that are not contained in the specified collection. If + * the specified collection is also a set, this operation effectively + * modifies this set so that its value is the <i>intersection</i> of the + * two sets. + * + * @param c collection containing elements to be retained in this set + * @return <tt>true</tt> if this set changed as a result of the call + * @throws ClassCastException if the class of an element of this set + * is incompatible with the specified collection (optional) + * @throws NullPointerException if this set contains a null element and the + * specified collection does not permit null elements (optional), + * or if the specified collection is null + * @see #remove(Object) + */ + public boolean retainAll(Collection<?> c) { + return al.retainAll(c); + } + + /** + * Returns an iterator over the elements contained in this set + * in the order in which these elements were added. + * + * <p>The returned iterator provides a snapshot of the state of the set + * when the iterator was constructed. No synchronization is needed while + * traversing the iterator. The iterator does <em>NOT</em> support the + * <tt>remove</tt> method. + * + * @return an iterator over the elements in this set + */ + public Iterator<E> iterator() { + return al.iterator(); + } + + /** + * Compares the specified object with this set for equality. + * Returns {@code true} if the specified object is the same object + * as this object, or if it is also a {@link Set} and the elements + * returned by an {@linkplain List#iterator() iterator} over the + * specified set are the same as the elements returned by an + * iterator over this set. More formally, the two iterators are + * considered to return the same elements if they return the same + * number of elements and for every element {@code e1} returned by + * the iterator over the specified set, there is an element + * {@code e2} returned by the iterator over this set such that + * {@code (e1==null ? e2==null : e1.equals(e2))}. + * + * @param o object to be compared for equality with this set + * @return {@code true} if the specified object is equal to this set + */ + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof Set)) + return false; + Set<?> set = (Set<?>)(o); + Iterator<?> it = set.iterator(); + + // Uses O(n^2) algorithm that is only appropriate + // for small sets, which CopyOnWriteArraySets should be. + + // Use a single snapshot of underlying array + Object[] elements = al.getArray(); + int len = elements.length; + // Mark matched elements to avoid re-checking + boolean[] matched = new boolean[len]; + int k = 0; + outer: while (it.hasNext()) { + if (++k > len) + return false; + Object x = it.next(); + for (int i = 0; i < len; ++i) { + if (!matched[i] && eq(x, elements[i])) { + matched[i] = true; + continue outer; + } + } + return false; + } + return k == len; + } + + /** + * Test for equality, coping with nulls. + */ + private static boolean eq(Object o1, Object o2) { + return (o1 == null ? o2 == null : o1.equals(o2)); + } } diff --git a/concurrent/src/main/java/java/util/concurrent/CountDownLatch.java b/concurrent/src/main/java/java/util/concurrent/CountDownLatch.java index 721744c..2b945dd 100644 --- a/concurrent/src/main/java/java/util/concurrent/CountDownLatch.java +++ b/concurrent/src/main/java/java/util/concurrent/CountDownLatch.java @@ -12,25 +12,25 @@ import java.util.concurrent.atomic.*; * A synchronization aid that allows one or more threads to wait until * a set of operations being performed in other threads completes. * - * <p>A <tt>CountDownLatch</tt> is initialized with a given - * <em>count</em>. The {@link #await await} methods block until the current - * {@link #getCount count} reaches zero due to invocations of the - * {@link #countDown} method, after which all waiting threads are - * released and any subsequent invocations of {@link #await await} return - * immediately. This is a one-shot phenomenon -- the count cannot be - * reset. If you need a version that resets the count, consider using - * a {@link CyclicBarrier}. + * <p>A {@code CountDownLatch} is initialized with a given <em>count</em>. + * The {@link #await await} methods block until the current count reaches + * zero due to invocations of the {@link #countDown} method, after which + * all waiting threads are released and any subsequent invocations of + * {@link #await await} return immediately. This is a one-shot phenomenon + * -- the count cannot be reset. If you need a version that resets the + * count, consider using a {@link CyclicBarrier}. * - * <p>A <tt>CountDownLatch</tt> is a versatile synchronization tool + * <p>A {@code CountDownLatch} is a versatile synchronization tool * and can be used for a number of purposes. A - * <tt>CountDownLatch</tt> initialized with a count of one serves as a + * {@code CountDownLatch} initialized with a count of one serves as a * simple on/off latch, or gate: all threads invoking {@link #await await} * wait at the gate until it is opened by a thread invoking {@link - * #countDown}. A <tt>CountDownLatch</tt> initialized to <em>N</em> + * #countDown}. A {@code CountDownLatch} initialized to <em>N</em> * can be used to make one thread wait until <em>N</em> threads have * completed some action, or some action has been completed N times. - * <p>A useful property of a <tt>CountDownLatch</tt> is that it - * doesn't require that threads calling <tt>countDown</tt> wait for + * + * <p>A useful property of a {@code CountDownLatch} is that it + * doesn't require that threads calling {@code countDown} wait for * the count to reach zero before proceeding, it simply prevents any * thread from proceeding past an {@link #await await} until all * threads could pass. @@ -119,6 +119,13 @@ import java.util.concurrent.atomic.*; * * </pre> * + * <p>Memory consistency effects: Until the count reaches + * zero, actions in a thread prior to calling + * {@code countDown()} + * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> + * actions following a successful return from a corresponding + * {@code await()} in another thread. + * * @since 1.5 * @author Doug Lea */ @@ -128,118 +135,120 @@ public class CountDownLatch { * Uses AQS state to represent count. */ private static final class Sync extends AbstractQueuedSynchronizer { + private static final long serialVersionUID = 4982264981922014374L; + Sync(int count) { - setState(count); + setState(count); } - + int getCount() { return getState(); } - public int tryAcquireShared(int acquires) { + protected int tryAcquireShared(int acquires) { return getState() == 0? 1 : -1; } - - public boolean tryReleaseShared(int releases) { + + protected boolean tryReleaseShared(int releases) { // Decrement count; signal when transition to zero for (;;) { int c = getState(); if (c == 0) return false; int nextc = c-1; - if (compareAndSetState(c, nextc)) + if (compareAndSetState(c, nextc)) return nextc == 0; } } } private final Sync sync; + /** - * Constructs a <tt>CountDownLatch</tt> initialized with the given - * count. - * - * @param count the number of times {@link #countDown} must be invoked - * before threads can pass through {@link #await}. + * Constructs a {@code CountDownLatch} initialized with the given count. * - * @throws IllegalArgumentException if <tt>count</tt> is less than zero. + * @param count the number of times {@link #countDown} must be invoked + * before threads can pass through {@link #await} + * @throws IllegalArgumentException if {@code count} is negative */ - public CountDownLatch(int count) { + public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); } /** - * Causes the current thread to wait until the latch has counted down to - * zero, unless the thread is {@link Thread#interrupt interrupted}. + * Causes the current thread to wait until the latch has counted down to + * zero, unless the thread is {@linkplain Thread#interrupt interrupted}. * - * <p>If the current {@link #getCount count} is zero then this method - * returns immediately. - * <p>If the current {@link #getCount count} is greater than zero then - * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until one of two things happen: + * <p>If the current count is zero then this method returns immediately. + * + * <p>If the current count is greater than zero then the current + * thread becomes disabled for thread scheduling purposes and lies + * dormant until one of two things happen: * <ul> * <li>The count reaches zero due to invocations of the * {@link #countDown} method; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread. + * <li>Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread. * </ul> + * * <p>If the current thread: * <ul> - * <li>has its interrupted status set on entry to this method; or - * <li>is {@link Thread#interrupt interrupted} while waiting, + * <li>has its interrupted status set on entry to this method; or + * <li>is {@linkplain Thread#interrupt interrupted} while waiting, * </ul> - * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. * * @throws InterruptedException if the current thread is interrupted - * while waiting. + * while waiting */ public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } /** - * Causes the current thread to wait until the latch has counted down to - * zero, unless the thread is {@link Thread#interrupt interrupted}, + * Causes the current thread to wait until the latch has counted down to + * zero, unless the thread is {@linkplain Thread#interrupt interrupted}, * or the specified waiting time elapses. * - * <p>If the current {@link #getCount count} is zero then this method - * returns immediately with the value <tt>true</tt>. + * <p>If the current count is zero then this method returns immediately + * with the value {@code true}. * - * <p>If the current {@link #getCount count} is greater than zero then - * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until one of three things happen: + * <p>If the current count is greater than zero then the current + * thread becomes disabled for thread scheduling purposes and lies + * dormant until one of three things happen: * <ul> * <li>The count reaches zero due to invocations of the * {@link #countDown} method; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + * <li>Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or * <li>The specified waiting time elapses. * </ul> + * * <p>If the count reaches zero then the method returns with the - * value <tt>true</tt>. + * value {@code true}. + * * <p>If the current thread: * <ul> - * <li>has its interrupted status set on entry to this method; or - * <li>is {@link Thread#interrupt interrupted} while waiting, + * <li>has its interrupted status set on entry to this method; or + * <li>is {@linkplain Thread#interrupt interrupted} while waiting, * </ul> - * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. * - * <p>If the specified waiting time elapses then the value <tt>false</tt> - * is returned. - * If the time is - * less than or equal to zero, the method will not wait at all. + * <p>If the specified waiting time elapses then the value {@code false} + * is returned. If the time is less than or equal to zero, the method + * will not wait at all. * * @param timeout the maximum time to wait - * @param unit the time unit of the <tt>timeout</tt> argument. - * @return <tt>true</tt> if the count reached zero and <tt>false</tt> - * if the waiting time elapsed before the count reached zero. - * + * @param unit the time unit of the {@code timeout} argument + * @return {@code true} if the count reached zero and {@code false} + * if the waiting time elapsed before the count reached zero * @throws InterruptedException if the current thread is interrupted - * while waiting. + * while waiting */ - public boolean await(long timeout, TimeUnit unit) + public boolean await(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); } @@ -247,11 +256,12 @@ public class CountDownLatch { /** * Decrements the count of the latch, releasing all waiting threads if * the count reaches zero. - * <p>If the current {@link #getCount count} is greater than zero then - * it is decremented. If the new count is zero then all waiting threads - * are re-enabled for thread scheduling purposes. - * <p>If the current {@link #getCount count} equals zero then nothing - * happens. + * + * <p>If the current count is greater than zero then it is decremented. + * If the new count is zero then all waiting threads are re-enabled for + * thread scheduling purposes. + * + * <p>If the current count equals zero then nothing happens. */ public void countDown() { sync.releaseShared(1); @@ -259,8 +269,10 @@ public class CountDownLatch { /** * Returns the current count. + * * <p>This method is typically used for debugging and testing purposes. - * @return the current count. + * + * @return the current count */ public long getCount() { return sync.getCount(); @@ -268,13 +280,12 @@ public class CountDownLatch { /** * Returns a string identifying this latch, as well as its state. - * The state, in brackets, includes the String - * "Count =" followed by the current count. - * @return a string identifying this latch, as well as its - * state + * The state, in brackets, includes the String {@code "Count ="} + * followed by the current count. + * + * @return a string identifying this latch, as well as its state */ public String toString() { return super.toString() + "[Count = " + sync.getCount() + "]"; } - } diff --git a/concurrent/src/main/java/java/util/concurrent/CyclicBarrier.java b/concurrent/src/main/java/java/util/concurrent/CyclicBarrier.java index d70c4c7..d5738c5 100644 --- a/concurrent/src/main/java/java/util/concurrent/CyclicBarrier.java +++ b/concurrent/src/main/java/java/util/concurrent/CyclicBarrier.java @@ -17,10 +17,10 @@ import java.util.concurrent.locks.*; * * <p>A <tt>CyclicBarrier</tt> supports an optional {@link Runnable} command * that is run once per barrier point, after the last thread in the party - * arrives, but before any threads are released. + * arrives, but before any threads are released. * This <em>barrier action</em> is useful * for updating shared-state before any of the parties continue. - * + * * <p><b>Sample usage:</b> Here is an example of * using a barrier in a parallel decomposition design: * <pre> @@ -28,7 +28,7 @@ import java.util.concurrent.locks.*; * final int N; * final float[][] data; * final CyclicBarrier barrier; - * + * * class Worker implements Runnable { * int myRow; * Worker(int row) { myRow = row; } @@ -37,11 +37,11 @@ import java.util.concurrent.locks.*; * processRow(myRow); * * try { - * barrier.await(); - * } catch (InterruptedException ex) { - * return; - * } catch (BrokenBarrierException ex) { - * return; + * barrier.await(); + * } catch (InterruptedException ex) { + * return; + * } catch (BrokenBarrierException ex) { + * return; * } * } * } @@ -50,22 +50,22 @@ import java.util.concurrent.locks.*; * public Solver(float[][] matrix) { * data = matrix; * N = matrix.length; - * barrier = new CyclicBarrier(N, + * barrier = new CyclicBarrier(N, * new Runnable() { - * public void run() { - * mergeRows(...); + * public void run() { + * mergeRows(...); * } * }); - * for (int i = 0; i < N; ++i) + * for (int i = 0; i < N; ++i) * new Thread(new Worker(i)).start(); * * waitUntilDone(); * } * } * </pre> - * Here, each worker thread processes a row of the matrix then waits at the + * Here, each worker thread processes a row of the matrix then waits at the * barrier until all rows have been processed. When all rows are processed - * the supplied {@link Runnable} barrier action is executed and merges the + * the supplied {@link Runnable} barrier action is executed and merges the * rows. If the merger * determines that a solution has been found then <tt>done()</tt> will return * <tt>true</tt> and each worker will terminate. @@ -74,19 +74,26 @@ import java.util.concurrent.locks.*; * it is executed, then any of the threads in the party could execute that * action when it is released. To facilitate this, each invocation of * {@link #await} returns the arrival index of that thread at the barrier. - * You can then choose which thread should execute the barrier action, for + * You can then choose which thread should execute the barrier action, for * example: * <pre> if (barrier.await() == 0) { * // log the completion of this iteration * }</pre> * - * <p>The <tt>CyclicBarrier</tt> uses a fast-fail all-or-none breakage - * model for failed synchronization attempts: If a thread leaves a - * barrier point prematurely because of interruption, failure, or - * timeout, all other threads, even those that have not yet resumed - * from a previous {@link #await}. will also leave abnormally via - * {@link BrokenBarrierException} (or <tt>InterruptedException</tt> if - * they too were interrupted at about the same time). + * <p>The <tt>CyclicBarrier</tt> uses an all-or-none breakage model + * for failed synchronization attempts: If a thread leaves a barrier + * point prematurely because of interruption, failure, or timeout, all + * other threads waiting at that barrier point will also leave + * abnormally via {@link BrokenBarrierException} (or + * {@link InterruptedException} if they too were interrupted at about + * the same time). + * + * <p>Memory consistency effects: Actions in a thread prior to calling + * {@code await()} + * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> + * actions that are part of the barrier action, which in turn + * <i>happen-before</i> actions following a successful return from the + * corresponding {@code await()} in other threads. * * @since 1.5 * @see CountDownLatch @@ -94,6 +101,21 @@ import java.util.concurrent.locks.*; * @author Doug Lea */ public class CyclicBarrier { + /** + * Each use of the barrier is represented as a generation instance. + * The generation changes whenever the barrier is tripped, or + * is reset. There can be many generations associated with threads + * using the barrier - due to the non-deterministic way the lock + * may be allocated to waiting threads - but only one of these + * can be active at a time (the one to which <tt>count</tt> applies) + * and all the rest are either broken or tripped. + * There need not be an active generation if there has been a break + * but no subsequent reset. + */ + private static class Generation { + boolean broken = false; + } + /** The lock for guarding barrier entry */ private final ReentrantLock lock = new ReentrantLock(); /** Condition to wait on until tripped */ @@ -102,53 +124,50 @@ public class CyclicBarrier { private final int parties; /* The command to run when tripped */ private final Runnable barrierCommand; - - /** - * The generation number. Incremented upon barrier trip. - * Retracted upon reset. - */ - private long generation; - - /** - * Breakage indicator. - */ - private boolean broken; + /** The current generation */ + private Generation generation = new Generation(); /** * Number of parties still waiting. Counts down from parties to 0 - * on each cycle. + * on each generation. It is reset to parties on each new + * generation or when broken. */ - private int count; + private int count; /** - * Updates state on barrier trip and wake up everyone. - */ + * Updates state on barrier trip and wakes up everyone. + * Called only while holding lock. + */ private void nextGeneration() { - count = parties; - ++generation; + // signal completion of last generation trip.signalAll(); + // set up next generation + count = parties; + generation = new Generation(); } /** - * Sets barrier as broken and wake up everyone + * Sets current barrier generation as broken and wakes up everyone. + * Called only while holding lock. */ private void breakBarrier() { - broken = true; + generation.broken = true; + count = parties; trip.signalAll(); } /** * Main barrier code, covering the various policies. */ - private int dowait(boolean timed, long nanos) - throws InterruptedException, BrokenBarrierException, TimeoutException { + private int dowait(boolean timed, long nanos) + throws InterruptedException, BrokenBarrierException, + TimeoutException { final ReentrantLock lock = this.lock; lock.lock(); try { - int index = --count; - long g = generation; + final Generation g = generation; - if (broken) + if (g.broken) throw new BrokenBarrierException(); if (Thread.interrupted()) { @@ -156,37 +175,45 @@ public class CyclicBarrier { throw new InterruptedException(); } - if (index == 0) { // tripped - nextGeneration(); - boolean ranAction = false; - try { - Runnable command = barrierCommand; - if (command != null) - command.run(); - ranAction = true; - return 0; - } finally { - if (!ranAction) - breakBarrier(); - } - } + int index = --count; + if (index == 0) { // tripped + boolean ranAction = false; + try { + final Runnable command = barrierCommand; + if (command != null) + command.run(); + ranAction = true; + nextGeneration(); + return 0; + } finally { + if (!ranAction) + breakBarrier(); + } + } + // loop until tripped, broken, interrupted, or timed out for (;;) { try { - if (!timed) + if (!timed) trip.await(); else if (nanos > 0L) nanos = trip.awaitNanos(nanos); } catch (InterruptedException ie) { - breakBarrier(); - throw ie; + if (g == generation && ! g.broken) { + breakBarrier(); + throw ie; + } else { + // We're about to finish waiting even if we had not + // been interrupted, so this interrupt is deemed to + // "belong" to subsequent execution. + Thread.currentThread().interrupt(); + } } - - if (broken || - g > generation) // true if a reset occurred while waiting + + if (g.broken) throw new BrokenBarrierException(); - if (g < generation) + if (g != generation) return index; if (timed && nanos <= 0L) { @@ -194,7 +221,6 @@ public class CyclicBarrier { throw new TimeoutException(); } } - } finally { lock.unlock(); } @@ -207,15 +233,14 @@ public class CyclicBarrier { * performed by the last thread entering the barrier. * * @param parties the number of threads that must invoke {@link #await} - * before the barrier is tripped. + * before the barrier is tripped * @param barrierAction the command to execute when the barrier is - * tripped, or <tt>null</tt> if there is no action. - * - * @throws IllegalArgumentException if <tt>parties</tt> is less than 1. + * tripped, or {@code null} if there is no action + * @throws IllegalArgumentException if {@code parties} is less than 1 */ public CyclicBarrier(int parties, Runnable barrierAction) { if (parties <= 0) throw new IllegalArgumentException(); - this.parties = parties; + this.parties = parties; this.count = parties; this.barrierCommand = barrierAction; } @@ -223,12 +248,11 @@ public class CyclicBarrier { /** * Creates a new <tt>CyclicBarrier</tt> that will trip when the * given number of parties (threads) are waiting upon it, and - * does not perform a predefined action upon each barrier. + * does not perform a predefined action when the barrier is tripped. * * @param parties the number of threads that must invoke {@link #await} - * before the barrier is tripped. - * - * @throws IllegalArgumentException if <tt>parties</tt> is less than 1. + * before the barrier is tripped + * @throws IllegalArgumentException if {@code parties} is less than 1 */ public CyclicBarrier(int parties) { this(parties, null); @@ -236,64 +260,66 @@ public class CyclicBarrier { /** * Returns the number of parties required to trip this barrier. - * @return the number of parties required to trip this barrier. - **/ + * + * @return the number of parties required to trip this barrier + */ public int getParties() { return parties; } /** - * Waits until all {@link #getParties parties} have invoked <tt>await</tt> - * on this barrier. + * Waits until all {@linkplain #getParties parties} have invoked + * <tt>await</tt> on this barrier. * * <p>If the current thread is not the last to arrive then it is * disabled for thread scheduling purposes and lies dormant until - * one of following things happens: + * one of the following things happens: * <ul> * <li>The last thread arrives; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread; or - * <li>Some other thread {@link Thread#interrupt interrupts} one of the - * other waiting threads; or + * <li>Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or + * <li>Some other thread {@linkplain Thread#interrupt interrupts} + * one of the other waiting threads; or * <li>Some other thread times out while waiting for barrier; or * <li>Some other thread invokes {@link #reset} on this barrier. * </ul> + * * <p>If the current thread: * <ul> * <li>has its interrupted status set on entry to this method; or - * <li>is {@link Thread#interrupt interrupted} while waiting + * <li>is {@linkplain Thread#interrupt interrupted} while waiting * </ul> * then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * - * <p>If the barrier is {@link #reset} while any thread is waiting, or if - * the barrier {@link #isBroken is broken} when <tt>await</tt> is invoked, - * or while any thread is waiting, - * then {@link BrokenBarrierException} is thrown. + * <p>If the barrier is {@link #reset} while any thread is waiting, + * or if the barrier {@linkplain #isBroken is broken} when + * <tt>await</tt> is invoked, or while any thread is waiting, then + * {@link BrokenBarrierException} is thrown. * - * <p>If any thread is {@link Thread#interrupt interrupted} while waiting, - * then all other waiting threads will throw + * <p>If any thread is {@linkplain Thread#interrupt interrupted} while waiting, + * then all other waiting threads will throw * {@link BrokenBarrierException} and the barrier is placed in the broken * state. * * <p>If the current thread is the last thread to arrive, and a * non-null barrier action was supplied in the constructor, then the - * current thread runs the action before allowing the other threads to + * current thread runs the action before allowing the other threads to * continue. * If an exception occurs during the barrier action then that exception * will be propagated in the current thread and the barrier is placed in * the broken state. * * @return the arrival index of the current thread, where index - * <tt>{@link #getParties()} - 1</tt> indicates the first to arrive and - * zero indicates the last to arrive. - * - * @throws InterruptedException if the current thread was interrupted - * while waiting + * <tt>{@link #getParties()} - 1</tt> indicates the first + * to arrive and zero indicates the last to arrive + * @throws InterruptedException if the current thread was interrupted + * while waiting * @throws BrokenBarrierException if <em>another</em> thread was - * interrupted while the current thread was waiting, or the barrier was - * reset, or the barrier was broken when <tt>await</tt> was called, - * or the barrier action (if present) failed due an exception. + * interrupted or timed out while the current thread was + * waiting, or the barrier was reset, or the barrier was + * broken when {@code await} was called, or the barrier + * action (if present) failed due an exception. */ public int await() throws InterruptedException, BrokenBarrierException { try { @@ -304,8 +330,8 @@ public class CyclicBarrier { } /** - * Waits until all {@link #getParties parties} have invoked <tt>await</tt> - * on this barrier. + * Waits until all {@linkplain #getParties parties} have invoked + * <tt>await</tt> on this barrier, or the specified waiting time elapses. * * <p>If the current thread is not the last to arrive then it is * disabled for thread scheduling purposes and lies dormant until @@ -313,34 +339,39 @@ public class CyclicBarrier { * <ul> * <li>The last thread arrives; or * <li>The specified timeout elapses; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread; or - * <li>Some other thread {@link Thread#interrupt interrupts} one of the - * other waiting threads; or + * <li>Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or + * <li>Some other thread {@linkplain Thread#interrupt interrupts} + * one of the other waiting threads; or * <li>Some other thread times out while waiting for barrier; or * <li>Some other thread invokes {@link #reset} on this barrier. * </ul> + * * <p>If the current thread: * <ul> * <li>has its interrupted status set on entry to this method; or - * <li>is {@link Thread#interrupt interrupted} while waiting + * <li>is {@linkplain Thread#interrupt interrupted} while waiting * </ul> * then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * - * <p>If the barrier is {@link #reset} while any thread is waiting, or if - * the barrier {@link #isBroken is broken} when <tt>await</tt> is invoked, - * or while any thread is waiting, - * then {@link BrokenBarrierException} is thrown. + * <p>If the specified waiting time elapses then {@link TimeoutException} + * is thrown. If the time is less than or equal to zero, the + * method will not wait at all. * - * <p>If any thread is {@link Thread#interrupt interrupted} while waiting, - * then all other waiting threads will throw - * {@link BrokenBarrierException} and the barrier is placed in the broken + * <p>If the barrier is {@link #reset} while any thread is waiting, + * or if the barrier {@linkplain #isBroken is broken} when + * <tt>await</tt> is invoked, or while any thread is waiting, then + * {@link BrokenBarrierException} is thrown. + * + * <p>If any thread is {@linkplain Thread#interrupt interrupted} while + * waiting, then all other waiting threads will throw {@link + * BrokenBarrierException} and the barrier is placed in the broken * state. * * <p>If the current thread is the last thread to arrive, and a * non-null barrier action was supplied in the constructor, then the - * current thread runs the action before allowing the other threads to + * current thread runs the action before allowing the other threads to * continue. * If an exception occurs during the barrier action then that exception * will be propagated in the current thread and the barrier is placed in @@ -349,36 +380,37 @@ public class CyclicBarrier { * @param timeout the time to wait for the barrier * @param unit the time unit of the timeout parameter * @return the arrival index of the current thread, where index - * <tt>{@link #getParties()} - 1</tt> indicates the first to arrive and - * zero indicates the last to arrive. - * - * @throws InterruptedException if the current thread was interrupted - * while waiting - * @throws TimeoutException if the specified timeout elapses. + * <tt>{@link #getParties()} - 1</tt> indicates the first + * to arrive and zero indicates the last to arrive + * @throws InterruptedException if the current thread was interrupted + * while waiting + * @throws TimeoutException if the specified timeout elapses * @throws BrokenBarrierException if <em>another</em> thread was - * interrupted while the current thread was waiting, or the barrier was - * reset, or the barrier was broken when <tt>await</tt> was called, - * or the barrier action (if present) failed due an exception. + * interrupted or timed out while the current thread was + * waiting, or the barrier was reset, or the barrier was broken + * when {@code await} was called, or the barrier action (if + * present) failed due an exception */ - public int await(long timeout, TimeUnit unit) - throws InterruptedException, - BrokenBarrierException, - TimeoutException { + public int await(long timeout, TimeUnit unit) + throws InterruptedException, + BrokenBarrierException, + TimeoutException { return dowait(true, unit.toNanos(timeout)); } /** * Queries if this barrier is in a broken state. - * @return <tt>true</tt> if one or more parties broke out of this - * barrier due to interruption or timeout since construction or - * the last reset, or a barrier action failed due to an exception; - * and <tt>false</tt> otherwise. + * + * @return {@code true} if one or more parties broke out of this + * barrier due to interruption or timeout since + * construction or the last reset, or a barrier action + * failed due to an exception; {@code false} otherwise. */ public boolean isBroken() { final ReentrantLock lock = this.lock; lock.lock(); try { - return broken; + return generation.broken; } finally { lock.unlock(); } @@ -397,15 +429,8 @@ public class CyclicBarrier { final ReentrantLock lock = this.lock; lock.lock(); try { - /* - * Retract generation number enough to cover threads - * currently waiting on current and still resuming from - * previous generation, plus similarly accommodating spans - * after the reset. - */ - generation -= 4; - broken = false; - trip.signalAll(); + breakBarrier(); // break the current generation + nextGeneration(); // start a new generation } finally { lock.unlock(); } @@ -416,7 +441,7 @@ public class CyclicBarrier { * This method is primarily useful for debugging and assertions. * * @return the number of parties currently blocked in {@link #await} - **/ + */ public int getNumberWaiting() { final ReentrantLock lock = this.lock; lock.lock(); @@ -426,5 +451,4 @@ public class CyclicBarrier { lock.unlock(); } } - } diff --git a/concurrent/src/main/java/java/util/concurrent/DelayQueue.java b/concurrent/src/main/java/java/util/concurrent/DelayQueue.java index 156dc7e..52ac4a0 100644 --- a/concurrent/src/main/java/java/util/concurrent/DelayQueue.java +++ b/concurrent/src/main/java/java/util/concurrent/DelayQueue.java @@ -14,14 +14,22 @@ import java.util.*; // END android-note /** - * An unbounded {@linkplain BlockingQueue blocking queue} of <tt>Delayed</tt> - * elements, in which an element can only be taken when its delay has expired. - * The <em>head</em> of the queue is that <tt>Delayed</tt> element whose delay - * expired furthest in the past - if no delay has expired there is no head and - * <tt>poll</tt> will return <tt>null</tt>. - * This queue does not permit <tt>null</tt> elements. - * <p>This class implements all of the <em>optional</em> methods - * of the {@link Collection} and {@link Iterator} interfaces. + * An unbounded {@linkplain BlockingQueue blocking queue} of + * <tt>Delayed</tt> elements, in which an element can only be taken + * when its delay has expired. The <em>head</em> of the queue is that + * <tt>Delayed</tt> element whose delay expired furthest in the + * past. If no delay has expired there is no head and <tt>poll</tt> + * will return <tt>null</tt>. Expiration occurs when an element's + * <tt>getDelay(TimeUnit.NANOSECONDS)</tt> method returns a value less + * than or equal to zero. Even though unexpired elements cannot be + * removed using <tt>take</tt> or <tt>poll</tt>, they are otherwise + * treated as normal elements. For example, the <tt>size</tt> method + * returns the count of both expired and unexpired elements. + * This queue does not permit null elements. + * + * <p>This class and its iterator implement all of the + * <em>optional</em> methods of the {@link Collection} and {@link + * Iterator} interfaces. * * @since 1.5 * @author Doug Lea @@ -32,10 +40,34 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> implements BlockingQueue<E> { private transient final ReentrantLock lock = new ReentrantLock(); - private transient final Condition available = lock.newCondition(); private final PriorityQueue<E> q = new PriorityQueue<E>(); /** + * Thread designated to wait for the element at the head of + * the queue. This variant of the Leader-Follower pattern + * (http://www.cs.wustl.edu/~schmidt/POSA/POSA2/) serves to + * minimize unnecessary timed waiting. When a thread becomes + * the leader, it waits only for the next delay to elapse, but + * other threads await indefinitely. The leader thread must + * signal some other thread before returning from take() or + * poll(...), unless some other thread becomes leader in the + * interim. Whenever the head of the queue is replaced with + * an element with an earlier expiration time, the leader + * field is invalidated by being reset to null, and some + * waiting thread, but not necessarily the current leader, is + * signalled. So waiting threads must be prepared to acquire + * and lose leadership while waiting. + */ + private Thread leader = null; + + /** + * Condition signalled when a newer element becomes available + * at the head of the queue or a new thread may need to + * become leader. + */ + private final Condition available = lock.newCondition(); + + /** * Creates a new <tt>DelayQueue</tt> that is initially empty. */ public DelayQueue() {} @@ -44,10 +76,9 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> * Creates a <tt>DelayQueue</tt> initially containing the elements of the * given collection of {@link Delayed} instances. * - * @param c the collection - * @throws NullPointerException if <tt>c</tt> or any element within it - * is <tt>null</tt> - * + * @param c the collection of elements to initially contain + * @throws NullPointerException if the specified collection or any + * of its elements are null */ public DelayQueue(Collection<? extends E> c) { this.addAll(c); @@ -56,91 +87,136 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> /** * Inserts the specified element into this delay queue. * - * @param o the element to add + * @param e the element to add + * @return <tt>true</tt> (as specified by {@link Collection#add}) + * @throws NullPointerException if the specified element is null + */ + public boolean add(E e) { + return offer(e); + } + + /** + * Inserts the specified element into this delay queue. + * + * @param e the element to add * @return <tt>true</tt> - * @throws NullPointerException if the specified element is <tt>null</tt>. + * @throws NullPointerException if the specified element is null */ - public boolean offer(E o) { + public boolean offer(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { - E first = q.peek(); - q.offer(o); - if (first == null || o.compareTo(first) < 0) - available.signalAll(); + q.offer(e); + if (q.peek() == e) { + leader = null; + available.signal(); + } return true; } finally { lock.unlock(); } } - /** - * Adds the specified element to this delay queue. As the queue is + * Inserts the specified element into this delay queue. As the queue is * unbounded this method will never block. - * @param o the element to add - * @throws NullPointerException if the specified element is <tt>null</tt>. + * + * @param e the element to add + * @throws NullPointerException {@inheritDoc} */ - public void put(E o) { - offer(o); + public void put(E e) { + offer(e); } /** * Inserts the specified element into this delay queue. As the queue is * unbounded this method will never block. - * @param o the element to add + * + * @param e the element to add * @param timeout This parameter is ignored as the method never blocks * @param unit This parameter is ignored as the method never blocks * @return <tt>true</tt> - * @throws NullPointerException if the specified element is <tt>null</tt>. + * @throws NullPointerException {@inheritDoc} */ - public boolean offer(E o, long timeout, TimeUnit unit) { - return offer(o); + public boolean offer(E e, long timeout, TimeUnit unit) { + return offer(e); } /** - * Adds the specified element to this queue. - * @param o the element to add - * @return <tt>true</tt> (as per the general contract of - * <tt>Collection.add</tt>). + * Retrieves and removes the head of this queue, or returns <tt>null</tt> + * if this queue has no elements with an expired delay. * - * @throws NullPointerException if the specified element is <tt>null</tt>. + * @return the head of this queue, or <tt>null</tt> if this + * queue has no elements with an expired delay */ - public boolean add(E o) { - return offer(o); + public E poll() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + E first = q.peek(); + if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0) + return null; + else + return q.poll(); + } finally { + lock.unlock(); + } } + /** + * Retrieves and removes the head of this queue, waiting if necessary + * until an element with an expired delay is available on this queue. + * + * @return the head of this queue + * @throws InterruptedException {@inheritDoc} + */ public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { for (;;) { E first = q.peek(); - if (first == null) { + if (first == null) available.await(); - } else { - long delay = first.getDelay(TimeUnit.NANOSECONDS); - if (delay > 0) { - long tl = available.awaitNanos(delay); - } else { - E x = q.poll(); - assert x != null; - if (q.size() != 0) - available.signalAll(); // wake up other takers - return x; - + else { + long delay = first.getDelay(TimeUnit.NANOSECONDS); + if (delay <= 0) + return q.poll(); + else if (leader != null) + available.await(); + else { + Thread thisThread = Thread.currentThread(); + leader = thisThread; + try { + available.awaitNanos(delay); + } finally { + if (leader == thisThread) + leader = null; + } } } } } finally { + if (leader == null && q.peek() != null) + available.signal(); lock.unlock(); } } - public E poll(long time, TimeUnit unit) throws InterruptedException { + /** + * Retrieves and removes the head of this queue, waiting if necessary + * until an element with an expired delay is available on this queue, + * or the specified wait time expires. + * + * @return the head of this queue, or <tt>null</tt> if the + * specified waiting time elapses before an element with + * an expired delay becomes available + * @throws InterruptedException {@inheritDoc} + */ + public E poll(long timeout, TimeUnit unit) throws InterruptedException { + long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); - long nanos = unit.toNanos(time); try { for (;;) { E first = q.peek(); @@ -150,46 +226,43 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> else nanos = available.awaitNanos(nanos); } else { - long delay = first.getDelay(TimeUnit.NANOSECONDS); - if (delay > 0) { - if (delay > nanos) - delay = nanos; - long timeLeft = available.awaitNanos(delay); - nanos -= delay - timeLeft; - } else { - E x = q.poll(); - assert x != null; - if (q.size() != 0) - available.signalAll(); - return x; + long delay = first.getDelay(TimeUnit.NANOSECONDS); + if (delay <= 0) + return q.poll(); + if (nanos <= 0) + return null; + if (nanos < delay || leader != null) + nanos = available.awaitNanos(nanos); + else { + Thread thisThread = Thread.currentThread(); + leader = thisThread; + try { + long timeLeft = available.awaitNanos(delay); + nanos -= delay - timeLeft; + } finally { + if (leader == thisThread) + leader = null; + } } } } } finally { + if (leader == null && q.peek() != null) + available.signal(); lock.unlock(); } } - - public E poll() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - E first = q.peek(); - if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0) - return null; - else { - E x = q.poll(); - assert x != null; - if (q.size() != 0) - available.signalAll(); - return x; - } - } finally { - lock.unlock(); - } - } - + /** + * Retrieves, but does not remove, the head of this queue, or + * returns <tt>null</tt> if this queue is empty. Unlike + * <tt>poll</tt>, if no expired elements are available in the queue, + * this method returns the element that will expire next, + * if one exists. + * + * @return the head of this queue, or <tt>null</tt> if this + * queue is empty. + */ public E peek() { final ReentrantLock lock = this.lock; lock.lock(); @@ -210,6 +283,12 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> } } + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection<? super E> c) { if (c == null) throw new NullPointerException(); @@ -226,14 +305,18 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> c.add(q.poll()); ++n; } - if (n > 0) - available.signalAll(); return n; } finally { lock.unlock(); } } + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection<? super E> c, int maxElements) { if (c == null) throw new NullPointerException(); @@ -252,8 +335,6 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> c.add(q.poll()); ++n; } - if (n > 0) - available.signalAll(); return n; } finally { lock.unlock(); @@ -263,6 +344,8 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> /** * Atomically removes all of the elements from this delay queue. * The queue will be empty after this call returns. + * Elements with an unexpired delay are not waited for; they are + * simply discarded from the queue. */ public void clear() { final ReentrantLock lock = this.lock; @@ -277,12 +360,26 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> /** * Always returns <tt>Integer.MAX_VALUE</tt> because * a <tt>DelayQueue</tt> is not capacity constrained. + * * @return <tt>Integer.MAX_VALUE</tt> */ public int remainingCapacity() { return Integer.MAX_VALUE; } + /** + * Returns an array containing all of the elements in this queue. + * The returned array elements are in no particular order. + * + * <p>The returned array will be "safe" in that no references to it are + * maintained by this queue. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + * <p>This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this queue + */ public Object[] toArray() { final ReentrantLock lock = this.lock; lock.lock(); @@ -293,16 +390,56 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> } } - public <T> T[] toArray(T[] array) { + /** + * Returns an array containing all of the elements in this queue; the + * runtime type of the returned array is that of the specified array. + * The returned array elements are in no particular order. + * If the queue fits in the specified array, it is returned therein. + * Otherwise, a new array is allocated with the runtime type of the + * specified array and the size of this queue. + * + * <p>If this queue fits in the specified array with room to spare + * (i.e., the array has more elements than this queue), the element in + * the array immediately following the end of the queue is set to + * <tt>null</tt>. + * + * <p>Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + * <p>The following code can be used to dump a delay queue into a newly + * allocated array of <tt>Delayed</tt>: + * + * <pre> + * Delayed[] a = q.toArray(new Delayed[0]);</pre> + * + * Note that <tt>toArray(new Object[0])</tt> is identical in function to + * <tt>toArray()</tt>. + * + * @param a the array into which the elements of the queue are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose + * @return an array containing all of the elements in this queue + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this queue + * @throws NullPointerException if the specified array is null + */ + public <T> T[] toArray(T[] a) { final ReentrantLock lock = this.lock; lock.lock(); try { - return q.toArray(array); + return q.toArray(a); } finally { lock.unlock(); } } + /** + * Removes a single instance of the specified element from this + * queue, if it is present, whether or not it has expired. + */ public boolean remove(Object o) { final ReentrantLock lock = this.lock; lock.lock(); @@ -314,49 +451,61 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> } /** - * Returns an iterator over the elements in this queue. The iterator - * does not return the elements in any particular order. The - * returned iterator is a thread-safe "fast-fail" iterator that will - * throw {@link java.util.ConcurrentModificationException} - * upon detected interference. + * Returns an iterator over all the elements (both expired and + * unexpired) in this queue. The iterator does not return the + * elements in any particular order. The returned + * <tt>Iterator</tt> is a "weakly consistent" iterator that will + * never throw {@link ConcurrentModificationException}, and + * guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed + * to) reflect any modifications subsequent to construction. * - * @return an iterator over the elements in this queue. + * @return an iterator over the elements in this queue */ public Iterator<E> iterator() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - return new Itr(q.iterator()); - } finally { - lock.unlock(); - } + return new Itr(toArray()); } - private class Itr<E> implements Iterator<E> { - private final Iterator<E> iter; - Itr(Iterator<E> i) { - iter = i; + /** + * Snapshot iterator that works off copy of underlying q array. + */ + private class Itr implements Iterator<E> { + final Object[] array; // Array of all elements + int cursor; // index of next element to return; + int lastRet; // index of last element, or -1 if no such + + Itr(Object[] array) { + lastRet = -1; + this.array = array; } public boolean hasNext() { - return iter.hasNext(); + return cursor < array.length; } + @SuppressWarnings("unchecked") public E next() { - final ReentrantLock lock = DelayQueue.this.lock; - lock.lock(); - try { - return iter.next(); - } finally { - lock.unlock(); - } + if (cursor >= array.length) + throw new NoSuchElementException(); + lastRet = cursor; + return (E)array[cursor++]; } public void remove() { - final ReentrantLock lock = DelayQueue.this.lock; + if (lastRet < 0) + throw new IllegalStateException(); + Object x = array[lastRet]; + lastRet = -1; + // Traverse underlying queue to find == element, + // not just a .equals element. lock.lock(); try { - iter.remove(); + for (Iterator it = q.iterator(); it.hasNext(); ) { + if (it.next() == x) { + it.remove(); + return; + } + } } finally { lock.unlock(); } diff --git a/concurrent/src/main/java/java/util/concurrent/Delayed.java b/concurrent/src/main/java/java/util/concurrent/Delayed.java index bd48292..af41300 100644 --- a/concurrent/src/main/java/java/util/concurrent/Delayed.java +++ b/concurrent/src/main/java/java/util/concurrent/Delayed.java @@ -4,6 +4,11 @@ * http://creativecommons.org/licenses/publicdomain */ +/* + * Modified in Apache Harmony to comply with Java 5 signature + * specification. + */ + package java.util.concurrent; import java.util.*; @@ -26,11 +31,12 @@ import java.util.*; public interface Delayed extends Comparable<Delayed> { /** - * Returns the delay associated with this object, in the given time unit. + * Returns the remaining delay associated with this object, in the + * given time unit. * * @param unit the time unit - * @return the delay; zero or negative values indicate that the - * delay has already elapsed + * @return the remaining delay; zero or negative values indicate + * that the delay has already elapsed */ long getDelay(TimeUnit unit); } diff --git a/concurrent/src/main/java/java/util/concurrent/Exchanger.java b/concurrent/src/main/java/java/util/concurrent/Exchanger.java index da8c815..f67659c 100644 --- a/concurrent/src/main/java/java/util/concurrent/Exchanger.java +++ b/concurrent/src/main/java/java/util/concurrent/Exchanger.java @@ -1,27 +1,31 @@ /* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at + * Written by Doug Lea, Bill Scherer, and Michael Scott 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 java.util.concurrent; -import java.util.concurrent.locks.*; +import java.util.concurrent.atomic.*; +import java.util.concurrent.locks.LockSupport; /** - * A synchronization point at which two threads can exchange objects. - * Each thread presents some object on entry to the {@link #exchange - * exchange} method, and receives the object presented by the other - * thread on return. + * A synchronization point at which threads can pair and swap elements + * within pairs. Each thread presents some object on entry to the + * {@link #exchange exchange} method, matches with a partner thread, + * and receives its partner's object on return. An Exchanger may be + * viewed as a bidirectional form of a {@link SynchronousQueue}. + * Exchangers may be useful in applications such as genetic algorithms + * and pipeline designs. * * <p><b>Sample Usage:</b> - * Here are the highlights of a class that uses an <tt>Exchanger</tt> to - * swap buffers between threads so that the thread filling the - * buffer gets a freshly - * emptied one when it needs it, handing off the filled one to - * the thread emptying the buffer. - * <pre> + * Here are the highlights of a class that uses an {@code Exchanger} + * to swap buffers between threads so that the thread filling the + * buffer gets a freshly emptied one when it needs it, handing off the + * filled one to the thread emptying the buffer. + * <pre>{@code * class FillAndEmpty { - * Exchanger<DataBuffer> exchanger = new Exchanger(); + * Exchanger<DataBuffer> exchanger = new Exchanger<DataBuffer>(); * DataBuffer initialEmptyBuffer = ... a made-up type * DataBuffer initialFullBuffer = ... * @@ -31,7 +35,7 @@ import java.util.concurrent.locks.*; * try { * while (currentBuffer != null) { * addToBuffer(currentBuffer); - * if (currentBuffer.full()) + * if (currentBuffer.isFull()) * currentBuffer = exchanger.exchange(currentBuffer); * } * } catch (InterruptedException ex) { ... handle ... } @@ -44,7 +48,7 @@ import java.util.concurrent.locks.*; * try { * while (currentBuffer != null) { * takeFromBuffer(currentBuffer); - * if (currentBuffer.empty()) + * if (currentBuffer.isEmpty()) * currentBuffer = exchanger.exchange(currentBuffer); * } * } catch (InterruptedException ex) { ... handle ...} @@ -56,192 +60,597 @@ import java.util.concurrent.locks.*; * new Thread(new EmptyingLoop()).start(); * } * } - * </pre> + * }</pre> + * + * <p>Memory consistency effects: For each pair of threads that + * successfully exchange objects via an {@code Exchanger}, actions + * prior to the {@code exchange()} in each thread + * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> + * those subsequent to a return from the corresponding {@code exchange()} + * in the other thread. * * @since 1.5 - * @author Doug Lea + * @author Doug Lea and Bill Scherer and Michael Scott * @param <V> The type of objects that may be exchanged */ public class Exchanger<V> { - private final ReentrantLock lock = new ReentrantLock(); - private final Condition taken = lock.newCondition(); + /* + * Algorithm Description: + * + * The basic idea is to maintain a "slot", which is a reference to + * a Node containing both an Item to offer and a "hole" waiting to + * get filled in. If an incoming "occupying" thread sees that the + * slot is null, it CAS'es (compareAndSets) a Node there and waits + * for another to invoke exchange. That second "fulfilling" thread + * sees that the slot is non-null, and so CASes it back to null, + * also exchanging items by CASing the hole, plus waking up the + * occupying thread if it is blocked. In each case CAS'es may + * fail because a slot at first appears non-null but is null upon + * CAS, or vice-versa. So threads may need to retry these + * actions. + * + * This simple approach works great when there are only a few + * threads using an Exchanger, but performance rapidly + * deteriorates due to CAS contention on the single slot when + * there are lots of threads using an exchanger. So instead we use + * an "arena"; basically a kind of hash table with a dynamically + * varying number of slots, any one of which can be used by + * threads performing an exchange. Incoming threads pick slots + * based on a hash of their Thread ids. If an incoming thread + * fails to CAS in its chosen slot, it picks an alternative slot + * instead. And similarly from there. If a thread successfully + * CASes into a slot but no other thread arrives, it tries + * another, heading toward the zero slot, which always exists even + * if the table shrinks. The particular mechanics controlling this + * are as follows: + * + * Waiting: Slot zero is special in that it is the only slot that + * exists when there is no contention. A thread occupying slot + * zero will block if no thread fulfills it after a short spin. + * In other cases, occupying threads eventually give up and try + * another slot. Waiting threads spin for a while (a period that + * should be a little less than a typical context-switch time) + * before either blocking (if slot zero) or giving up (if other + * slots) and restarting. There is no reason for threads to block + * unless there are unlikely to be any other threads present. + * Occupants are mainly avoiding memory contention so sit there + * quietly polling for a shorter period than it would take to + * block and then unblock them. Non-slot-zero waits that elapse + * because of lack of other threads waste around one extra + * context-switch time per try, which is still on average much + * faster than alternative approaches. + * + * Sizing: Usually, using only a few slots suffices to reduce + * contention. Especially with small numbers of threads, using + * too many slots can lead to just as poor performance as using + * too few of them, and there's not much room for error. The + * variable "max" maintains the number of slots actually in + * use. It is increased when a thread sees too many CAS + * failures. (This is analogous to resizing a regular hash table + * based on a target load factor, except here, growth steps are + * just one-by-one rather than proportional.) Growth requires + * contention failures in each of three tried slots. Requiring + * multiple failures for expansion copes with the fact that some + * failed CASes are not due to contention but instead to simple + * races between two threads or thread pre-emptions occurring + * between reading and CASing. Also, very transient peak + * contention can be much higher than the average sustainable + * levels. The max limit is decreased on average 50% of the times + * that a non-slot-zero wait elapses without being fulfilled. + * Threads experiencing elapsed waits move closer to zero, so + * eventually find existing (or future) threads even if the table + * has been shrunk due to inactivity. The chosen mechanics and + * thresholds for growing and shrinking are intrinsically + * entangled with indexing and hashing inside the exchange code, + * and can't be nicely abstracted out. + * + * Hashing: Each thread picks its initial slot to use in accord + * with a simple hashcode. The sequence is the same on each + * encounter by any given thread, but effectively random across + * threads. Using arenas encounters the classic cost vs quality + * tradeoffs of all hash tables. Here, we use a one-step FNV-1a + * hash code based on the current thread's Thread.getId(), along + * with a cheap approximation to a mod operation to select an + * index. The downside of optimizing index selection in this way + * is that the code is hardwired to use a maximum table size of + * 32. But this value more than suffices for known platforms and + * applications. + * + * Probing: On sensed contention of a selected slot, we probe + * sequentially through the table, analogously to linear probing + * after collision in a hash table. (We move circularly, in + * reverse order, to mesh best with table growth and shrinkage + * rules.) Except that to minimize the effects of false-alarms + * and cache thrashing, we try the first selected slot twice + * before moving. + * + * Padding: Even with contention management, slots are heavily + * contended, so use cache-padding to avoid poor memory + * performance. Because of this, slots are lazily constructed + * only when used, to avoid wasting this space unnecessarily. + * While isolation of locations is not much of an issue at first + * in an application, as time goes on and garbage-collectors + * perform compaction, slots are very likely to be moved adjacent + * to each other, which can cause much thrashing of cache lines on + * MPs unless padding is employed. + * + * This is an improvement of the algorithm described in the paper + * "A Scalable Elimination-based Exchange Channel" by William + * Scherer, Doug Lea, and Michael Scott in Proceedings of SCOOL05 + * workshop. Available at: http://hdl.handle.net/1802/2104 + */ + + /** The number of CPUs, for sizing and spin control */ + private static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** + * The capacity of the arena. Set to a value that provides more + * than enough space to handle contention. On small machines + * most slots won't be used, but it is still not wasted because + * the extra space provides some machine-level address padding + * to minimize interference with heavily CAS'ed Slot locations. + * And on very large machines, performance eventually becomes + * bounded by memory bandwidth, not numbers of threads/CPUs. + * This constant cannot be changed without also modifying + * indexing and hashing algorithms. + */ + private static final int CAPACITY = 32; - /** Holder for the item being exchanged */ - private V item; - /** - * Arrival count transitions from 0 to 1 to 2 then back to 0 - * during an exchange. + * The value of "max" that will hold all threads without + * contention. When this value is less than CAPACITY, some + * otherwise wasted expansion can be avoided. */ - private int arrivalCount; + private static final int FULL = + Math.max(0, Math.min(CAPACITY, NCPU / 2) - 1); + + /** + * The number of times to spin (doing nothing except polling a + * memory location) before blocking or giving up while waiting to + * be fulfilled. Should be zero on uniprocessors. On + * multiprocessors, this value should be large enough so that two + * threads exchanging items as fast as possible block only when + * one of them is stalled (due to GC or preemption), but not much + * longer, to avoid wasting CPU resources. Seen differently, this + * value is a little over half the number of cycles of an average + * context switch time on most systems. The value here is + * approximately the average of those across a range of tested + * systems. + */ + private static final int SPINS = (NCPU == 1) ? 0 : 2000; + + /** + * The number of times to spin before blocking in timed waits. + * Timed waits spin more slowly because checking the time takes + * time. The best value relies mainly on the relative rate of + * System.nanoTime vs memory accesses. The value is empirically + * derived to work well across a variety of systems. + */ + private static final int TIMED_SPINS = SPINS / 20; + + /** + * Sentinel item representing cancellation of a wait due to + * interruption, timeout, or elapsed spin-waits. This value is + * placed in holes on cancellation, and used as a return value + * from waiting methods to indicate failure to set or get hole. + */ + private static final Object CANCEL = new Object(); + + /** + * Value representing null arguments/returns from public + * methods. This disambiguates from internal requirement that + * holes start out as null to mean they are not yet set. + */ + private static final Object NULL_ITEM = new Object(); + + /** + * Nodes hold partially exchanged data. This class + * opportunistically subclasses AtomicReference to represent the + * hole. So get() returns hole, and compareAndSet CAS'es value + * into hole. This class cannot be parameterized as "V" because + * of the use of non-V CANCEL sentinels. + */ + private static final class Node extends AtomicReference<Object> { + /** The element offered by the Thread creating this node. */ + public final Object item; + + /** The Thread waiting to be signalled; null until waiting. */ + public volatile Thread waiter; + + /** + * Creates node with given item and empty hole. + * @param item the item + */ + public Node(Object item) { + this.item = item; + } + } + + /** + * A Slot is an AtomicReference with heuristic padding to lessen + * cache effects of this heavily CAS'ed location. While the + * padding adds noticeable space, all slots are created only on + * demand, and there will be more than one of them only when it + * would improve throughput more than enough to outweigh using + * extra space. + */ + private static final class Slot extends AtomicReference<Object> { + // Improve likelihood of isolation on <= 64 byte cache lines + long q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, qa, qb, qc, qd, qe; + } + + /** + * Slot array. Elements are lazily initialized when needed. + * Declared volatile to enable double-checked lazy construction. + */ + private volatile Slot[] arena = new Slot[CAPACITY]; + + /** + * The maximum slot index being used. The value sometimes + * increases when a thread experiences too many CAS contentions, + * and sometimes decreases when a spin-wait elapses. Changes + * are performed only via compareAndSet, to avoid stale values + * when a thread happens to stall right before setting. + */ + private final AtomicInteger max = new AtomicInteger(); /** * Main exchange function, handling the different policy variants. + * Uses Object, not "V" as argument and return value to simplify + * handling of sentinel values. Callers from public methods decode + * and cast accordingly. + * + * @param item the (non-null) item to exchange + * @param timed true if the wait is timed + * @param nanos if timed, the maximum wait time + * @return the other thread's item, or CANCEL if interrupted or timed out */ - private V doExchange(V x, boolean timed, long nanos) throws InterruptedException, TimeoutException { - lock.lock(); - try { - V other; - - // If arrival count already at two, we must wait for - // a previous pair to finish and reset the count; - while (arrivalCount == 2) { - if (!timed) - taken.await(); - else if (nanos > 0) - nanos = taken.awaitNanos(nanos); - else - throw new TimeoutException(); + private Object doExchange(Object item, boolean timed, long nanos) { + Node me = new Node(item); // Create in case occupying + int index = hashIndex(); // Index of current slot + int fails = 0; // Number of CAS failures + + for (;;) { + Object y; // Contents of current slot + Slot slot = arena[index]; + if (slot == null) // Lazily initialize slots + createSlot(index); // Continue loop to reread + else if ((y = slot.get()) != null && // Try to fulfill + slot.compareAndSet(y, null)) { + Node you = (Node)y; // Transfer item + if (you.compareAndSet(null, item)) { + LockSupport.unpark(you.waiter); + return you.item; + } // Else cancelled; continue + } + else if (y == null && // Try to occupy + slot.compareAndSet(null, me)) { + if (index == 0) // Blocking wait for slot 0 + return timed? awaitNanos(me, slot, nanos): await(me, slot); + Object v = spinWait(me, slot); // Spin wait for non-0 + if (v != CANCEL) + return v; + me = new Node(item); // Throw away cancelled node + int m = max.get(); + if (m > (index >>>= 1)) // Decrease index + max.compareAndSet(m, m - 1); // Maybe shrink table + } + else if (++fails > 1) { // Allow 2 fails on 1st slot + int m = max.get(); + if (fails > 3 && m < FULL && max.compareAndSet(m, m + 1)) + index = m + 1; // Grow on 3rd failed slot + else if (--index < 0) + index = m; // Circularly traverse } + } + } - int count = ++arrivalCount; + /** + * Returns a hash index for the current thread. Uses a one-step + * FNV-1a hash code (http://www.isthe.com/chongo/tech/comp/fnv/) + * based on the current thread's Thread.getId(). These hash codes + * have more uniform distribution properties with respect to small + * moduli (here 1-31) than do other simple hashing functions. + * + * <p>To return an index between 0 and max, we use a cheap + * approximation to a mod operation, that also corrects for bias + * due to non-power-of-2 remaindering (see {@link + * java.util.Random#nextInt}). Bits of the hashcode are masked + * with "nbits", the ceiling power of two of table size (looked up + * in a table packed into three ints). If too large, this is + * retried after rotating the hash by nbits bits, while forcing new + * top bit to 0, which guarantees eventual termination (although + * with a non-random-bias). This requires an average of less than + * 2 tries for all table sizes, and has a maximum 2% difference + * from perfectly uniform slot probabilities when applied to all + * possible hash codes for sizes less than 32. + * + * @return a per-thread-random index, 0 <= index < max + */ + private final int hashIndex() { + long id = Thread.currentThread().getId(); + int hash = (((int)(id ^ (id >>> 32))) ^ 0x811c9dc5) * 0x01000193; - // If item is already waiting, replace it and signal other thread - if (count == 2) { - other = item; - item = x; - taken.signal(); - return other; - } + int m = max.get(); + int nbits = (((0xfffffc00 >> m) & 4) | // Compute ceil(log2(m+1)) + ((0x000001f8 >>> m) & 2) | // The constants hold + ((0xffff00f2 >>> m) & 1)); // a lookup table + int index; + while ((index = hash & ((1 << nbits) - 1)) > m) // May retry on + hash = (hash >>> nbits) | (hash << (33 - nbits)); // non-power-2 m + return index; + } - // Otherwise, set item and wait for another thread to - // replace it and signal us. - - item = x; - InterruptedException interrupted = null; - try { - while (arrivalCount != 2) { - if (!timed) - taken.await(); - else if (nanos > 0) - nanos = taken.awaitNanos(nanos); - else - break; // timed out - } - } catch (InterruptedException ie) { - interrupted = ie; - } + /** + * Creates a new slot at given index. Called only when the slot + * appears to be null. Relies on double-check using builtin + * locks, since they rarely contend. This in turn relies on the + * arena array being declared volatile. + * + * @param index the index to add slot at + */ + private void createSlot(int index) { + // Create slot outside of lock to narrow sync region + Slot newSlot = new Slot(); + Slot[] a = arena; + synchronized (a) { + if (a[index] == null) + a[index] = newSlot; + } + } + + /** + * Tries to cancel a wait for the given node waiting in the given + * slot, if so, helping clear the node from its slot to avoid + * garbage retention. + * + * @param node the waiting node + * @param slot the slot it is waiting in + * @return true if successfully cancelled + */ + private static boolean tryCancel(Node node, Slot slot) { + if (!node.compareAndSet(null, CANCEL)) + return false; + if (slot.get() == node) // pre-check to minimize contention + slot.compareAndSet(node, null); + return true; + } + + // Three forms of waiting. Each just different enough not to merge + // code with others. + + /** + * Spin-waits for hole for a non-0 slot. Fails if spin elapses + * before hole filled. Does not check interrupt, relying on check + * in public exchange method to abort if interrupted on entry. + * + * @param node the waiting node + * @return on success, the hole; on failure, CANCEL + */ + private static Object spinWait(Node node, Slot slot) { + int spins = SPINS; + for (;;) { + Object v = node.get(); + if (v != null) + return v; + else if (spins > 0) + --spins; + else + tryCancel(node, slot); + } + } + + /** + * Waits for (by spinning and/or blocking) and gets the hole + * filled in by another thread. Fails if interrupted before + * hole filled. + * + * When a node/thread is about to block, it sets its waiter field + * and then rechecks state at least one more time before actually + * parking, thus covering race vs fulfiller noticing that waiter + * is non-null so should be woken. + * + * Thread interruption status is checked only surrounding calls to + * park. The caller is assumed to have checked interrupt status + * on entry. + * + * @param node the waiting node + * @return on success, the hole; on failure, CANCEL + */ + private static Object await(Node node, Slot slot) { + Thread w = Thread.currentThread(); + int spins = SPINS; + for (;;) { + Object v = node.get(); + if (v != null) + return v; + else if (spins > 0) // Spin-wait phase + --spins; + else if (node.waiter == null) // Set up to block next + node.waiter = w; + else if (w.isInterrupted()) // Abort on interrupt + tryCancel(node, slot); + else // Block + LockSupport.park(); + } + } - // Get and reset item and count after the wait. - // (We need to do this even if wait was aborted.) - other = item; - item = null; - count = arrivalCount; - arrivalCount = 0; - taken.signal(); - - // If the other thread replaced item, then we must - // continue even if cancelled. - if (count == 2) { - if (interrupted != null) - Thread.currentThread().interrupt(); - return other; + /** + * Waits for (at index 0) and gets the hole filled in by another + * thread. Fails if timed out or interrupted before hole filled. + * Same basic logic as untimed version, but a bit messier. + * + * @param node the waiting node + * @param nanos the wait time + * @return on success, the hole; on failure, CANCEL + */ + private Object awaitNanos(Node node, Slot slot, long nanos) { + int spins = TIMED_SPINS; + long lastTime = 0; + Thread w = null; + for (;;) { + Object v = node.get(); + if (v != null) + return v; + long now = System.nanoTime(); + if (w == null) + w = Thread.currentThread(); + else + nanos -= now - lastTime; + lastTime = now; + if (nanos > 0) { + if (spins > 0) + --spins; + else if (node.waiter == null) + node.waiter = w; + else if (w.isInterrupted()) + tryCancel(node, slot); + else + LockSupport.parkNanos(nanos); } + else if (tryCancel(node, slot) && !w.isInterrupted()) + return scanOnTimeout(node); + } + } - // If no one is waiting for us, we can back out - if (interrupted != null) - throw interrupted; - else // must be timeout - throw new TimeoutException(); - } finally { - lock.unlock(); + /** + * Sweeps through arena checking for any waiting threads. Called + * only upon return from timeout while waiting in slot 0. When a + * thread gives up on a timed wait, it is possible that a + * previously-entered thread is still waiting in some other + * slot. So we scan to check for any. This is almost always + * overkill, but decreases the likelihood of timeouts when there + * are other threads present to far less than that in lock-based + * exchangers in which earlier-arriving threads may still be + * waiting on entry locks. + * + * @param node the waiting node + * @return another thread's item, or CANCEL + */ + private Object scanOnTimeout(Node node) { + Object y; + for (int j = arena.length - 1; j >= 0; --j) { + Slot slot = arena[j]; + if (slot != null) { + while ((y = slot.get()) != null) { + if (slot.compareAndSet(y, null)) { + Node you = (Node)y; + if (you.compareAndSet(null, node.item)) { + LockSupport.unpark(you.waiter); + return you.item; + } + } + } + } } + return CANCEL; } /** - * Create a new Exchanger. - **/ + * Creates a new Exchanger. + */ public Exchanger() { } /** * Waits for another thread to arrive at this exchange point (unless - * it is {@link Thread#interrupt interrupted}), + * the current thread is {@linkplain Thread#interrupt interrupted}), * and then transfers the given object to it, receiving its object * in return. + * * <p>If another thread is already waiting at the exchange point then * it is resumed for thread scheduling purposes and receives the object - * passed in by the current thread. The current thread returns immediately, + * passed in by the current thread. The current thread returns immediately, * receiving the object passed to the exchange by that other thread. - * <p>If no other thread is already waiting at the exchange then the + * + * <p>If no other thread is already waiting at the exchange then the * current thread is disabled for thread scheduling purposes and lies * dormant until one of two things happens: * <ul> * <li>Some other thread enters the exchange; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current + * <li>Some other thread {@linkplain Thread#interrupt interrupts} the current * thread. * </ul> * <p>If the current thread: * <ul> - * <li>has its interrupted status set on entry to this method; or - * <li>is {@link Thread#interrupt interrupted} while waiting - * for the exchange, + * <li>has its interrupted status set on entry to this method; or + * <li>is {@linkplain Thread#interrupt interrupted} while waiting + * for the exchange, * </ul> - * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. * * @param x the object to exchange - * @return the object provided by the other thread. - * @throws InterruptedException if current thread was interrupted - * while waiting - **/ + * @return the object provided by the other thread + * @throws InterruptedException if the current thread was + * interrupted while waiting + */ public V exchange(V x) throws InterruptedException { - try { - return doExchange(x, false, 0); - } catch (TimeoutException cannotHappen) { - throw new Error(cannotHappen); + if (!Thread.interrupted()) { + Object v = doExchange(x == null? NULL_ITEM : x, false, 0); + if (v == NULL_ITEM) + return null; + if (v != CANCEL) + return (V)v; + Thread.interrupted(); // Clear interrupt status on IE throw } + throw new InterruptedException(); } /** * Waits for another thread to arrive at this exchange point (unless - * it is {@link Thread#interrupt interrupted}, or the specified waiting - * time elapses), - * and then transfers the given object to it, receiving its object - * in return. + * the current thread is {@linkplain Thread#interrupt interrupted} or + * the specified waiting time elapses), and then transfers the given + * object to it, receiving its object in return. * * <p>If another thread is already waiting at the exchange point then * it is resumed for thread scheduling purposes and receives the object - * passed in by the current thread. The current thread returns immediately, + * passed in by the current thread. The current thread returns immediately, * receiving the object passed to the exchange by that other thread. * - * <p>If no other thread is already waiting at the exchange then the + * <p>If no other thread is already waiting at the exchange then the * current thread is disabled for thread scheduling purposes and lies * dormant until one of three things happens: * <ul> * <li>Some other thread enters the exchange; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + * <li>Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or * <li>The specified waiting time elapses. * </ul> * <p>If the current thread: * <ul> - * <li>has its interrupted status set on entry to this method; or - * <li>is {@link Thread#interrupt interrupted} while waiting - * for the exchange, + * <li>has its interrupted status set on entry to this method; or + * <li>is {@linkplain Thread#interrupt interrupted} while waiting + * for the exchange, * </ul> - * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. * - * <p>If the specified waiting time elapses then {@link TimeoutException} - * is thrown. - * If the time is - * less than or equal to zero, the method will not wait at all. + * <p>If the specified waiting time elapses then {@link + * TimeoutException} is thrown. If the time is less than or equal + * to zero, the method will not wait at all. * * @param x the object to exchange * @param timeout the maximum time to wait - * @param unit the time unit of the <tt>timeout</tt> argument. - * @return the object provided by the other thread. - * @throws InterruptedException if current thread was interrupted - * while waiting - * @throws TimeoutException if the specified waiting time elapses before - * another thread enters the exchange. - **/ - public V exchange(V x, long timeout, TimeUnit unit) + * @param unit the time unit of the <tt>timeout</tt> argument + * @return the object provided by the other thread + * @throws InterruptedException if the current thread was + * interrupted while waiting + * @throws TimeoutException if the specified waiting time elapses + * before another thread enters the exchange + */ + public V exchange(V x, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { - return doExchange(x, true, unit.toNanos(timeout)); + if (!Thread.interrupted()) { + Object v = doExchange(x == null? NULL_ITEM : x, + true, unit.toNanos(timeout)); + if (v == NULL_ITEM) + return null; + if (v != CANCEL) + return (V)v; + if (!Thread.interrupted()) + throw new TimeoutException(); + } + throw new InterruptedException(); } - } - - diff --git a/concurrent/src/main/java/java/util/concurrent/ExecutionException.java b/concurrent/src/main/java/java/util/concurrent/ExecutionException.java index 8f0e448..bc561e5 100644 --- a/concurrent/src/main/java/java/util/concurrent/ExecutionException.java +++ b/concurrent/src/main/java/java/util/concurrent/ExecutionException.java @@ -19,14 +19,14 @@ public class ExecutionException extends Exception { private static final long serialVersionUID = 7830266012832686185L; /** - * Constructs a <tt>ExecutionException</tt> with no detail message. + * Constructs an <tt>ExecutionException</tt> with no detail message. * The cause is not initialized, and may subsequently be * initialized by a call to {@link #initCause(Throwable) initCause}. */ protected ExecutionException() { } /** - * Constructs a <tt>ExecutionException</tt> with the specified detail + * Constructs an <tt>ExecutionException</tt> with the specified detail * message. The cause is not initialized, and may subsequently be * initialized by a call to {@link #initCause(Throwable) initCause}. * @@ -37,7 +37,7 @@ public class ExecutionException extends Exception { } /** - * Constructs a <tt>ExecutionException</tt> with the specified detail + * Constructs an <tt>ExecutionException</tt> with the specified detail * message and cause. * * @param message the detail message @@ -49,7 +49,7 @@ public class ExecutionException extends Exception { } /** - * Constructs a <tt>ExecutionException</tt> with the specified cause. + * Constructs an <tt>ExecutionException</tt> with the specified cause. * The detail message is set to: * <pre> * (cause == null ? null : cause.toString())</pre> diff --git a/concurrent/src/main/java/java/util/concurrent/Executor.java b/concurrent/src/main/java/java/util/concurrent/Executor.java index 60fe193..a61e921 100644 --- a/concurrent/src/main/java/java/util/concurrent/Executor.java +++ b/concurrent/src/main/java/java/util/concurrent/Executor.java @@ -21,7 +21,7 @@ package java.util.concurrent; * executor.execute(new RunnableTask2()); * ... * </pre> - * + * * However, the <tt>Executor</tt> interface does not strictly * require that execution be asynchronous. In the simplest case, an * executor can run the submitted task immediately in the caller's @@ -52,7 +52,7 @@ package java.util.concurrent; * * <pre> * class SerialExecutor implements Executor { - * final Queue<Runnable> tasks = new LinkedBlockingQueue<Runnable>(); + * final Queue<Runnable> tasks = new ArrayDeque<Runnable>(); * final Executor executor; * Runnable active; * @@ -88,6 +88,11 @@ package java.util.concurrent; * extensible thread pool implementation. The {@link Executors} class * provides convenient factory methods for these Executors. * + * <p>Memory consistency effects: Actions in a thread prior to + * submitting a {@code Runnable} object to an {@code Executor} + * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> + * its execution begins, perhaps in another thread. + * * @since 1.5 * @author Doug Lea */ diff --git a/concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java b/concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java index 4f9c9d9..02606f9 100644 --- a/concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java +++ b/concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java @@ -6,11 +6,10 @@ package java.util.concurrent; - /** * A {@link CompletionService} that uses a supplied {@link Executor} * to execute tasks. This class arranges that submitted tasks are, - * upon completion, placed on a queue accessible using <tt>take</tt>. + * upon completion, placed on a queue accessible using {@code take}. * The class is lightweight enough to be suitable for transient use * when processing groups of tasks. * @@ -19,84 +18,102 @@ package java.util.concurrent; * <b>Usage Examples.</b> * * Suppose you have a set of solvers for a certain problem, each - * returning a value of some type <tt>Result</tt>, and would like to + * returning a value of some type {@code Result}, and would like to * run them concurrently, processing the results of each of them that - * return a non-null value, in some method <tt>use(Result r)</tt>. You + * return a non-null value, in some method {@code use(Result r)}. You * could write this as: * - * <pre> - * void solve(Executor e, Collection<Callable<Result>> solvers) - * throws InterruptedException, ExecutionException { - * CompletionService<Result> ecs = new ExecutorCompletionService<Result>(e); - * for (Callable<Result> s : solvers) - * ecs.submit(s); - * int n = solvers.size(); - * for (int i = 0; i < n; ++i) { - * Result r = ecs.take().get(); - * if (r != null) - * use(r); - * } - * } - * </pre> + * <pre> {@code + * void solve(Executor e, + * Collection<Callable<Result>> solvers) + * throws InterruptedException, ExecutionException { + * CompletionService<Result> ecs + * = new ExecutorCompletionService<Result>(e); + * for (Callable<Result> s : solvers) + * ecs.submit(s); + * int n = solvers.size(); + * for (int i = 0; i < n; ++i) { + * Result r = ecs.take().get(); + * if (r != null) + * use(r); + * } + * }}</pre> * * Suppose instead that you would like to use the first non-null result * of the set of tasks, ignoring any that encounter exceptions, * and cancelling all other tasks when the first one is ready: * - * <pre> - * void solve(Executor e, Collection<Callable<Result>> solvers) - * throws InterruptedException { - * CompletionService<Result> ecs = new ExecutorCompletionService<Result>(e); - * int n = solvers.size(); - * List<Future<Result>> futures = new ArrayList<Future<Result>>(n); - * Result result = null; - * try { - * for (Callable<Result> s : solvers) - * futures.add(ecs.submit(s)); - * for (int i = 0; i < n; ++i) { - * try { - * Result r = ecs.take().get(); - * if (r != null) { - * result = r; - * break; - * } - * } catch(ExecutionException ignore) {} - * } - * } - * finally { - * for (Future<Result> f : futures) - * f.cancel(true); - * } + * <pre> {@code + * void solve(Executor e, + * Collection<Callable<Result>> solvers) + * throws InterruptedException { + * CompletionService<Result> ecs + * = new ExecutorCompletionService<Result>(e); + * int n = solvers.size(); + * List<Future<Result>> futures + * = new ArrayList<Future<Result>>(n); + * Result result = null; + * try { + * for (Callable<Result> s : solvers) + * futures.add(ecs.submit(s)); + * for (int i = 0; i < n; ++i) { + * try { + * Result r = ecs.take().get(); + * if (r != null) { + * result = r; + * break; + * } + * } catch (ExecutionException ignore) {} + * } + * } + * finally { + * for (Future<Result> f : futures) + * f.cancel(true); + * } * - * if (result != null) - * use(result); - * } - * </pre> + * if (result != null) + * use(result); + * }}</pre> */ public class ExecutorCompletionService<V> implements CompletionService<V> { private final Executor executor; + private final AbstractExecutorService aes; private final BlockingQueue<Future<V>> completionQueue; /** * FutureTask extension to enqueue upon completion */ - private class QueueingFuture extends FutureTask<V> { - QueueingFuture(Callable<V> c) { super(c); } - QueueingFuture(Runnable t, V r) { super(t, r); } - protected void done() { completionQueue.add(this); } + private class QueueingFuture extends FutureTask<Void> { + QueueingFuture(FutureTask<V> task) { + super(task, null); + this.task = task; + } + protected void done() { completionQueue.add(task); } + private final Future<V> task; + } + + private FutureTask<V> newTaskFor(Callable<V> task) { + return new FutureTask<V>(task); + } + + private FutureTask<V> newTaskFor(Runnable task, V result) { + return new FutureTask<V>(task, result); } /** * Creates an ExecutorCompletionService using the supplied * executor for base task execution and a * {@link LinkedBlockingQueue} as a completion queue. + * * @param executor the executor to use - * @throws NullPointerException if executor is <tt>null</tt> + * @throws NullPointerException if executor is {@code null} */ public ExecutorCompletionService(Executor executor) { - if (executor == null) + if (executor == null) throw new NullPointerException(); this.executor = executor; + this.aes = (executor instanceof AbstractExecutorService) ? + (AbstractExecutorService) executor : null; this.completionQueue = new LinkedBlockingQueue<Future<V>>(); } @@ -104,30 +121,36 @@ public class ExecutorCompletionService<V> implements CompletionService<V> { * Creates an ExecutorCompletionService using the supplied * executor for base task execution and the supplied queue as its * completion queue. + * * @param executor the executor to use * @param completionQueue the queue to use as the completion queue - * normally one dedicated for use by this service - * @throws NullPointerException if executor or completionQueue are <tt>null</tt> + * normally one dedicated for use by this service. This + * queue is treated as unbounded -- failed attempted + * {@code Queue.add} operations for completed taskes cause + * them not to be retrievable. + * @throws NullPointerException if executor or completionQueue are {@code null} */ public ExecutorCompletionService(Executor executor, BlockingQueue<Future<V>> completionQueue) { - if (executor == null || completionQueue == null) + if (executor == null || completionQueue == null) throw new NullPointerException(); this.executor = executor; + this.aes = (executor instanceof AbstractExecutorService) ? + (AbstractExecutorService) executor : null; this.completionQueue = completionQueue; } public Future<V> submit(Callable<V> task) { if (task == null) throw new NullPointerException(); - QueueingFuture f = new QueueingFuture(task); - executor.execute(f); + FutureTask<V> f = newTaskFor(task); + executor.execute(new QueueingFuture(f)); return f; } public Future<V> submit(Runnable task, V result) { if (task == null) throw new NullPointerException(); - QueueingFuture f = new QueueingFuture(task, result); - executor.execute(f); + FutureTask<V> f = newTaskFor(task, result); + executor.execute(new QueueingFuture(f)); return f; } @@ -144,5 +167,3 @@ public class ExecutorCompletionService<V> implements CompletionService<V> { } } - - diff --git a/concurrent/src/main/java/java/util/concurrent/ExecutorService.java b/concurrent/src/main/java/java/util/concurrent/ExecutorService.java index e178eb2..757f7cb 100644 --- a/concurrent/src/main/java/java/util/concurrent/ExecutorService.java +++ b/concurrent/src/main/java/java/util/concurrent/ExecutorService.java @@ -5,7 +5,6 @@ */ package java.util.concurrent; - import java.util.List; import java.util.Collection; import java.security.PrivilegedAction; @@ -14,14 +13,18 @@ import java.security.PrivilegedExceptionAction; /** * An {@link Executor} that provides methods to manage termination and * methods that can produce a {@link Future} for tracking progress of - * one or more asynchronous tasks. + * one or more asynchronous tasks. * - * <p> - * An <tt>ExecutorService</tt> can be shut down, which will cause it - * to stop accepting new tasks. After being shut down, the executor - * will eventually terminate, at which point no tasks are actively - * executing, no tasks are awaiting execution, and no new tasks can be - * submitted. + * <p> An <tt>ExecutorService</tt> can be shut down, which will cause + * it to reject new tasks. Two different methods are provided for + * shutting down an <tt>ExecutorService</tt>. The {@link #shutdown} + * method will allow previously submitted tasks to execute before + * terminating, while the {@link #shutdownNow} method prevents waiting + * tasks from starting and attempts to stop currently executing tasks. + * Upon termination, an executor has no tasks actively executing, no + * tasks awaiting execution, and no new tasks can be submitted. An + * unused <tt>ExecutorService</tt> should be shut down to allow + * reclamation of its resources. * * <p> Method <tt>submit</tt> extends base method {@link * Executor#execute} by creating and returning a {@link Future} that @@ -35,41 +38,74 @@ import java.security.PrivilegedExceptionAction; * <p>The {@link Executors} class provides factory methods for the * executor services provided in this package. * - * <h3>Usage Example</h3> + * <h3>Usage Examples</h3> * * Here is a sketch of a network service in which threads in a thread * pool service incoming requests. It uses the preconfigured {@link * Executors#newFixedThreadPool} factory method: * * <pre> - * class NetworkService { - * private final ServerSocket serverSocket; - * private final ExecutorService pool; + * class NetworkService implements Runnable { + * private final ServerSocket serverSocket; + * private final ExecutorService pool; + * + * public NetworkService(int port, int poolSize) + * throws IOException { + * serverSocket = new ServerSocket(port); + * pool = Executors.newFixedThreadPool(poolSize); + * } + * + * public void run() { // run the service + * try { + * for (;;) { + * pool.execute(new Handler(serverSocket.accept())); + * } + * } catch (IOException ex) { + * pool.shutdown(); + * } + * } + * } + * + * class Handler implements Runnable { + * private final Socket socket; + * Handler(Socket socket) { this.socket = socket; } + * public void run() { + * // read and service request on socket + * } + * } + * </pre> * - * public NetworkService(int port, int poolSize) throws IOException { - * serverSocket = new ServerSocket(port); - * pool = Executors.newFixedThreadPool(poolSize); - * } - * - * public void serve() { - * try { - * for (;;) { - * pool.execute(new Handler(serverSocket.accept())); - * } - * } catch (IOException ex) { - * pool.shutdown(); - * } - * } - * } + * The following method shuts down an <tt>ExecutorService</tt> in two phases, + * first by calling <tt>shutdown</tt> to reject incoming tasks, and then + * calling <tt>shutdownNow</tt>, if necessary, to cancel any lingering tasks: * - * class Handler implements Runnable { - * private final Socket socket; - * Handler(Socket socket) { this.socket = socket; } - * public void run() { - * // read and service request - * } + * <pre> + * void shutdownAndAwaitTermination(ExecutorService pool) { + * pool.shutdown(); // Disable new tasks from being submitted + * try { + * // Wait a while for existing tasks to terminate + * if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { + * pool.shutdownNow(); // Cancel currently executing tasks + * // Wait a while for tasks to respond to being cancelled + * if (!pool.awaitTermination(60, TimeUnit.SECONDS)) + * System.err.println("Pool did not terminate"); + * } + * } catch (InterruptedException ie) { + * // (Re-)Cancel if current thread also interrupted + * pool.shutdownNow(); + * // Preserve interrupt status + * Thread.currentThread().interrupt(); + * } * } * </pre> + * + * <p>Memory consistency effects: Actions in a thread prior to the + * submission of a {@code Runnable} or {@code Callable} task to an + * {@code ExecutorService} + * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> + * any actions taken by that task, which in turn <i>happen-before</i> the + * result is retrieved via {@code Future.get()}. + * * @since 1.5 * @author Doug Lea */ @@ -77,33 +113,45 @@ public interface ExecutorService extends Executor { /** * Initiates an orderly shutdown in which previously submitted - * tasks are executed, but no new tasks will be - * accepted. Invocation has no additional effect if already shut - * down. + * tasks are executed, but no new tasks will be accepted. + * Invocation has no additional effect if already shut down. + * + * <p>This method does not wait for previously submitted tasks to + * complete execution. Use {@link #awaitTermination awaitTermination} + * to do that. + * * @throws SecurityException if a security manager exists and - * shutting down this ExecutorService may manipulate threads that - * the caller is not permitted to modify because it does not hold - * {@link java.lang.RuntimePermission}<tt>("modifyThread")</tt>, - * or the security manager's <tt>checkAccess</tt> method denies access. + * shutting down this ExecutorService may manipulate + * threads that the caller is not permitted to modify + * because it does not hold {@link + * java.lang.RuntimePermission}<tt>("modifyThread")</tt>, + * or the security manager's <tt>checkAccess</tt> method + * denies access. */ void shutdown(); /** * Attempts to stop all actively executing tasks, halts the - * processing of waiting tasks, and returns a list of the tasks that were - * awaiting execution. - * + * processing of waiting tasks, and returns a list of the tasks + * that were awaiting execution. + * + * <p>This method does not wait for actively executing tasks to + * terminate. Use {@link #awaitTermination awaitTermination} to + * do that. + * * <p>There are no guarantees beyond best-effort attempts to stop * processing actively executing tasks. For example, typical - * implementations will cancel via {@link Thread#interrupt}, so if any - * tasks mask or fail to respond to interrupts, they may never terminate. + * implementations will cancel via {@link Thread#interrupt}, so any + * task that fails to respond to interrupts may never terminate. * * @return list of tasks that never commenced execution * @throws SecurityException if a security manager exists and - * shutting down this ExecutorService may manipulate threads that - * the caller is not permitted to modify because it does not hold - * {@link java.lang.RuntimePermission}<tt>("modifyThread")</tt>, - * or the security manager's <tt>checkAccess</tt> method denies access. + * shutting down this ExecutorService may manipulate + * threads that the caller is not permitted to modify + * because it does not hold {@link + * java.lang.RuntimePermission}<tt>("modifyThread")</tt>, + * or the security manager's <tt>checkAccess</tt> method + * denies access. */ List<Runnable> shutdownNow(); @@ -130,8 +178,8 @@ public interface ExecutorService extends Executor { * * @param timeout the maximum time to wait * @param unit the time unit of the timeout argument - * @return <tt>true</tt> if this executor terminated and <tt>false</tt> - * if the timeout elapsed before termination + * @return <tt>true</tt> if this executor terminated and + * <tt>false</tt> if the timeout elapsed before termination * @throws InterruptedException if interrupted while waiting */ boolean awaitTermination(long timeout, TimeUnit unit) @@ -139,8 +187,10 @@ public interface ExecutorService extends Executor { /** - * Submits a value-returning task for execution and returns a Future - * representing the pending results of the task. + * Submits a value-returning task for execution and returns a + * Future representing the pending results of the task. The + * Future's <tt>get</tt> method will return the task's result upon + * successful completion. * * <p> * If you would like to immediately block waiting @@ -154,88 +204,92 @@ public interface ExecutorService extends Executor { * * @param task the task to submit * @return a Future representing pending completion of the task - * @throws RejectedExecutionException if task cannot be scheduled - * for execution - * @throws NullPointerException if task null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + * @throws NullPointerException if the task is null */ <T> Future<T> submit(Callable<T> task); /** - * Submits a Runnable task for execution and returns a Future - * representing that task that will upon completion return - * the given result + * Submits a Runnable task for execution and returns a Future + * representing that task. The Future's <tt>get</tt> method will + * return the given result upon successful completion. * * @param task the task to submit * @param result the result to return - * @return a Future representing pending completion of the task, - * and whose <tt>get()</tt> method will return the given result - * upon completion. - * @throws RejectedExecutionException if task cannot be scheduled - * for execution - * @throws NullPointerException if task null + * @return a Future representing pending completion of the task + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + * @throws NullPointerException if the task is null */ <T> Future<T> submit(Runnable task, T result); /** - * Submits a Runnable task for execution and returns a Future - * representing that task. + * Submits a Runnable task for execution and returns a Future + * representing that task. The Future's <tt>get</tt> method will + * return <tt>null</tt> upon <em>successful</em> completion. * * @param task the task to submit - * @return a Future representing pending completion of the task, - * and whose <tt>get()</tt> method will return <tt>null</tt> - * upon completion. - * @throws RejectedExecutionException if task cannot be scheduled - * for execution - * @throws NullPointerException if task null + * @return a Future representing pending completion of the task + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + * @throws NullPointerException if the task is null */ Future<?> submit(Runnable task); /** - * Executes the given tasks, returning their results - * when all complete. + * Executes the given tasks, returning a list of Futures holding + * their status and results when all complete. + * {@link Future#isDone} is <tt>true</tt> for each + * element of the returned list. * Note that a <em>completed</em> task could have * terminated either normally or by throwing an exception. * The results of this method are undefined if the given * collection is modified while this operation is in progress. + * * @param tasks the collection of tasks * @return A list of Futures representing the tasks, in the same - * sequential order as produced by the iterator for the given task - * list, each of which has completed. + * sequential order as produced by the iterator for the + * given task list, each of which has completed. * @throws InterruptedException if interrupted while waiting, in - * which case unfinished tasks are cancelled. + * which case unfinished tasks are cancelled. * @throws NullPointerException if tasks or any of its elements are <tt>null</tt> - * @throws RejectedExecutionException if any task cannot be scheduled - * for execution + * @throws RejectedExecutionException if any task cannot be + * scheduled for execution */ <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks) throws InterruptedException; /** - * Executes the given tasks, returning their results + * Executes the given tasks, returning a list of Futures holding + * their status and results * when all complete or the timeout expires, whichever happens first. + * {@link Future#isDone} is <tt>true</tt> for each + * element of the returned list. * Upon return, tasks that have not completed are cancelled. * Note that a <em>completed</em> task could have * terminated either normally or by throwing an exception. * The results of this method are undefined if the given * collection is modified while this operation is in progress. + * * @param tasks the collection of tasks * @param timeout the maximum time to wait * @param unit the time unit of the timeout argument - * @return A list of Futures representing the tasks, in the same - * sequential order as produced by the iterator for the given - * task list. If the operation did not time out, each task will - * have completed. If it did time out, some of thiese tasks will - * not have completed. + * @return a list of Futures representing the tasks, in the same + * sequential order as produced by the iterator for the + * given task list. If the operation did not time out, + * each task will have completed. If it did time out, some + * of these tasks will not have completed. * @throws InterruptedException if interrupted while waiting, in - * which case unfinished tasks are cancelled. + * which case unfinished tasks are cancelled * @throws NullPointerException if tasks, any of its elements, or - * unit are <tt>null</tt> + * unit are <tt>null</tt> * @throws RejectedExecutionException if any task cannot be scheduled - * for execution + * for execution */ - <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks, - long timeout, TimeUnit unit) + <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks, + long timeout, TimeUnit unit) throws InterruptedException; /** @@ -245,15 +299,16 @@ public interface ExecutorService extends Executor { * tasks that have not completed are cancelled. * The results of this method are undefined if the given * collection is modified while this operation is in progress. + * * @param tasks the collection of tasks - * @return The result returned by one of the tasks. + * @return the result returned by one of the tasks * @throws InterruptedException if interrupted while waiting * @throws NullPointerException if tasks or any of its elements - * are <tt>null</tt> - * @throws IllegalArgumentException if tasks empty + * are <tt>null</tt> + * @throws IllegalArgumentException if tasks is empty * @throws ExecutionException if no task successfully completes * @throws RejectedExecutionException if tasks cannot be scheduled - * for execution + * for execution */ <T> T invokeAny(Collection<Callable<T>> tasks) throws InterruptedException, ExecutionException; @@ -266,21 +321,21 @@ public interface ExecutorService extends Executor { * completed are cancelled. * The results of this method are undefined if the given * collection is modified while this operation is in progress. + * * @param tasks the collection of tasks * @param timeout the maximum time to wait * @param unit the time unit of the timeout argument - * @return The result returned by one of the tasks. + * @return the result returned by one of the tasks. * @throws InterruptedException if interrupted while waiting * @throws NullPointerException if tasks, any of its elements, or - * unit are <tt>null</tt> + * unit are <tt>null</tt> * @throws TimeoutException if the given timeout elapses before - * any task successfully completes + * any task successfully completes * @throws ExecutionException if no task successfully completes * @throws RejectedExecutionException if tasks cannot be scheduled - * for execution + * for execution */ - <T> T invokeAny(Collection<Callable<T>> tasks, - long timeout, TimeUnit unit) + <T> T invokeAny(Collection<Callable<T>> tasks, + long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; - } diff --git a/concurrent/src/main/java/java/util/concurrent/Executors.java b/concurrent/src/main/java/java/util/concurrent/Executors.java index 23f1b75..90cf6ee 100644 --- a/concurrent/src/main/java/java/util/concurrent/Executors.java +++ b/concurrent/src/main/java/java/util/concurrent/Executors.java @@ -11,6 +11,7 @@ import java.security.AccessControlContext; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedActionException; import java.security.AccessControlException; /** @@ -18,18 +19,18 @@ import java.security.AccessControlException; * ExecutorService}, {@link ScheduledExecutorService}, {@link * ThreadFactory}, and {@link Callable} classes defined in this * package. This class supports the following kinds of methods: - * + * * <ul> - * <li> Methods that create and return an {@link ExecutorService} - * set up with commonly useful configuration settings. - * <li> Methods that create and return a {@link ScheduledExecutorService} - * set up with commonly useful configuration settings. + * <li> Methods that create and return an {@link ExecutorService} + * set up with commonly useful configuration settings. + * <li> Methods that create and return a {@link ScheduledExecutorService} + * set up with commonly useful configuration settings. * <li> Methods that create and return a "wrapped" ExecutorService, that * disables reconfiguration by making implementation-specific methods * inaccessible. * <li> Methods that create and return a {@link ThreadFactory} * that sets newly created threads to a known state. - * <li> Methods that create and return a {@link Callable} + * <li> Methods that create and return a {@link Callable} * out of other closure-like forms, so they can be used * in execution methods requiring <tt>Callable</tt>. * </ul> @@ -40,14 +41,19 @@ import java.security.AccessControlException; public class Executors { /** - * Creates a thread pool that reuses a fixed set of threads - * operating off a shared unbounded queue. If any thread - * terminates due to a failure during execution prior to shutdown, - * a new one will take its place if needed to execute subsequent - * tasks. + * Creates a thread pool that reuses a fixed number of threads + * operating off a shared unbounded queue. At any point, at most + * <tt>nThreads</tt> threads will be active processing tasks. + * If additional tasks are submitted when all threads are active, + * they will wait in the queue until a thread is available. + * If any thread terminates due to a failure during execution + * prior to shutdown, a new one will take its place if needed to + * execute subsequent tasks. The threads in the pool will exist + * until it is explicitly {@link ExecutorService#shutdown shutdown}. * * @param nThreads the number of threads in the pool * @return the newly created thread pool + * @throws IllegalArgumentException if <tt>nThreads <= 0</tt> */ public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, @@ -56,13 +62,23 @@ public class Executors { } /** - * Creates a thread pool that reuses a fixed set of threads + * Creates a thread pool that reuses a fixed number of threads * operating off a shared unbounded queue, using the provided - * ThreadFactory to create new threads when needed. + * ThreadFactory to create new threads when needed. At any point, + * at most <tt>nThreads</tt> threads will be active processing + * tasks. If additional tasks are submitted when all threads are + * active, they will wait in the queue until a thread is + * available. If any thread terminates due to a failure during + * execution prior to shutdown, a new one will take its place if + * needed to execute subsequent tasks. The threads in the pool will + * exist until it is explicitly {@link ExecutorService#shutdown + * shutdown}. * * @param nThreads the number of threads in the pool * @param threadFactory the factory to use when creating new threads * @return the newly created thread pool + * @throws NullPointerException if threadFactory is null + * @throws IllegalArgumentException if <tt>nThreads <= 0</tt> */ public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { return new ThreadPoolExecutor(nThreads, nThreads, @@ -85,7 +101,7 @@ public class Executors { * @return the newly created single-threaded Executor */ public static ExecutorService newSingleThreadExecutor() { - return new DelegatedExecutorService + return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); @@ -95,17 +111,18 @@ public class Executors { * Creates an Executor that uses a single worker thread operating * off an unbounded queue, and uses the provided ThreadFactory to * create a new thread when needed. Unlike the otherwise - * equivalent <tt>newFixedThreadPool(1, threadFactory)</tt> the returned executor - * is guaranteed not to be reconfigurable to use additional - * threads. - * + * equivalent <tt>newFixedThreadPool(1, threadFactory)</tt> the + * returned executor is guaranteed not to be reconfigurable to use + * additional threads. + * * @param threadFactory the factory to use when creating new * threads * * @return the newly created single-threaded Executor + * @throws NullPointerException if threadFactory is null */ public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { - return new DelegatedExecutorService + return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), @@ -141,6 +158,7 @@ public class Executors { * ThreadFactory to create new threads when needed. * @param threadFactory the factory to use when creating new threads * @return the newly created thread pool + * @throws NullPointerException if threadFactory is null */ public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, @@ -148,7 +166,7 @@ public class Executors { new SynchronousQueue<Runnable>(), threadFactory); } - + /** * Creates a single-threaded executor that can schedule commands * to run after a given delay, or to execute periodically. @@ -181,31 +199,35 @@ public class Executors { * @param threadFactory the factory to use when creating new * threads * @return a newly created scheduled executor + * @throws NullPointerException if threadFactory is null */ public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) { return new DelegatedScheduledExecutorService (new ScheduledThreadPoolExecutor(1, threadFactory)); } - + /** - * Creates a thread pool that can schedule commands to run after a + * Creates a thread pool that can schedule commands to run after a * given delay, or to execute periodically. * @param corePoolSize the number of threads to keep in the pool, * even if they are idle. * @return a newly created scheduled thread pool + * @throws IllegalArgumentException if <tt>corePoolSize < 0</tt> */ public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } /** - * Creates a thread pool that can schedule commands to run after a + * Creates a thread pool that can schedule commands to run after a * given delay, or to execute periodically. * @param corePoolSize the number of threads to keep in the pool, * even if they are idle. * @param threadFactory the factory to use when the executor - * creates a new thread. + * creates a new thread. * @return a newly created scheduled thread pool + * @throws IllegalArgumentException if <tt>corePoolSize < 0</tt> + * @throws NullPointerException if threadFactory is null */ public static ScheduledExecutorService newScheduledThreadPool( int corePoolSize, ThreadFactory threadFactory) { @@ -244,7 +266,7 @@ public class Executors { throw new NullPointerException(); return new DelegatedScheduledExecutorService(executor); } - + /** * Returns a default thread factory used to create new threads. * This factory creates all new threads used by an Executor in the @@ -252,8 +274,9 @@ public class Executors { * java.lang.SecurityManager}, it uses the group of {@link * System#getSecurityManager}, else the group of the thread * invoking this <tt>defaultThreadFactory</tt> method. Each new - * thread is created as a non-daemon thread with priority - * <tt>Thread.NORM_PRIORITY</tt>. New threads have names + * thread is created as a non-daemon thread with priority set to + * the smaller of <tt>Thread.NORM_PRIORITY</tt> and the maximum + * priority permitted in the thread group. New threads have names * accessible via {@link Thread#getName} of * <em>pool-N-thread-M</em>, where <em>N</em> is the sequence * number of this factory, and <em>M</em> is the sequence number @@ -307,8 +330,8 @@ public class Executors { * <tt>Callable</tt> to an otherwise resultless action. * @param task the task to run * @param result the result to return - * @throws NullPointerException if task null * @return a callable object + * @throws NullPointerException if task null */ public static <T> Callable<T> callable(Runnable task, T result) { if (task == null) @@ -336,10 +359,11 @@ public class Executors { * @return a callable object * @throws NullPointerException if action null */ - public static Callable<Object> callable(PrivilegedAction action) { + public static Callable<Object> callable(final PrivilegedAction<?> action) { if (action == null) throw new NullPointerException(); - return new PrivilegedActionAdapter(action); + return new Callable<Object>() { + public Object call() { return action.run(); }}; } /** @@ -350,10 +374,11 @@ public class Executors { * @return a callable object * @throws NullPointerException if action null */ - public static Callable<Object> callable(PrivilegedExceptionAction action) { + public static Callable<Object> callable(final PrivilegedExceptionAction<?> action) { if (action == null) throw new NullPointerException(); - return new PrivilegedExceptionActionAdapter(action); + return new Callable<Object>() { + public Object call() throws Exception { return action.run(); }}; } /** @@ -373,9 +398,9 @@ public class Executors { public static <T> Callable<T> privilegedCallable(Callable<T> callable) { if (callable == null) throw new NullPointerException(); - return new PrivilegedCallable(callable); + return new PrivilegedCallable<T>(callable); } - + /** * Returns a {@link Callable} object that will, when * called, execute the given <tt>callable</tt> under the current @@ -397,7 +422,7 @@ public class Executors { public static <T> Callable<T> privilegedCallableUsingCurrentClassLoader(Callable<T> callable) { if (callable == null) throw new NullPointerException(); - return new PrivilegedCallableUsingCurrentClassLoader(callable); + return new PrivilegedCallableUsingCurrentClassLoader<T>(callable); } // Non-public classes supporting the public methods @@ -408,71 +433,39 @@ public class Executors { static final class RunnableAdapter<T> implements Callable<T> { final Runnable task; final T result; - RunnableAdapter(Runnable task, T result) { - this.task = task; + RunnableAdapter(Runnable task, T result) { + this.task = task; this.result = result; } - public T call() { - task.run(); - return result; - } - } - - /** - * A callable that runs given privileged action and returns its result - */ - static final class PrivilegedActionAdapter implements Callable<Object> { - PrivilegedActionAdapter(PrivilegedAction action) { - this.action = action; - } - public Object call () { - return action.run(); - } - private final PrivilegedAction action; - } - - /** - * A callable that runs given privileged exception action and returns its result - */ - static final class PrivilegedExceptionActionAdapter implements Callable<Object> { - PrivilegedExceptionActionAdapter(PrivilegedExceptionAction action) { - this.action = action; - } - public Object call () throws Exception { - return action.run(); + public T call() { + task.run(); + return result; } - private final PrivilegedExceptionAction action; } - /** * A callable that runs under established access control settings */ static final class PrivilegedCallable<T> implements Callable<T> { - private final AccessControlContext acc; private final Callable<T> task; - private T result; - private Exception exception; + private final AccessControlContext acc; + PrivilegedCallable(Callable<T> task) { this.task = task; this.acc = AccessController.getContext(); } public T call() throws Exception { - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - try { - result = task.call(); - } catch(Exception ex) { - exception = ex; + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction<T>() { + public T run() throws Exception { + return task.call(); } - return null; - } - }, acc); - if (exception != null) - throw exception; - else - return result; + }, acc); + } catch (PrivilegedActionException e) { + throw e.getException(); + } } } @@ -481,44 +474,50 @@ public class Executors { * current ClassLoader */ static final class PrivilegedCallableUsingCurrentClassLoader<T> implements Callable<T> { - private final ClassLoader ccl; - private final AccessControlContext acc; private final Callable<T> task; - private T result; - private Exception exception; + private final AccessControlContext acc; + private final ClassLoader ccl; + PrivilegedCallableUsingCurrentClassLoader(Callable<T> task) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + // Calls to getContextClassLoader from this class + // never trigger a security check, but we check + // whether our callers have this permission anyways. + sm.checkPermission(new RuntimePermission("getContextClassLoader")); + + // Whether setContextClassLoader turns out to be necessary + // or not, we fail fast if permission is not available. + sm.checkPermission(new RuntimePermission("setContextClassLoader")); + } this.task = task; - this.ccl = Thread.currentThread().getContextClassLoader(); this.acc = AccessController.getContext(); - acc.checkPermission(new RuntimePermission("getContextClassLoader")); - acc.checkPermission(new RuntimePermission("setContextClassLoader")); + this.ccl = Thread.currentThread().getContextClassLoader(); } public T call() throws Exception { - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - ClassLoader savedcl = null; - Thread t = Thread.currentThread(); - try { - ClassLoader cl = t.getContextClassLoader(); - if (ccl != cl) { - t.setContextClassLoader(ccl); - savedcl = cl; + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction<T>() { + public T run() throws Exception { + ClassLoader savedcl = null; + Thread t = Thread.currentThread(); + try { + ClassLoader cl = t.getContextClassLoader(); + if (ccl != cl) { + t.setContextClassLoader(ccl); + savedcl = cl; + } + return task.call(); + } finally { + if (savedcl != null) + t.setContextClassLoader(savedcl); } - result = task.call(); - } catch(Exception ex) { - exception = ex; - } finally { - if (savedcl != null) - t.setContextClassLoader(savedcl); } - return null; - } - }, acc); - if (exception != null) - throw exception; - else - return result; + }, acc); + } catch (PrivilegedActionException e) { + throw e.getException(); + } } } @@ -526,22 +525,22 @@ public class Executors { * The default thread factory */ static class DefaultThreadFactory implements ThreadFactory { - static final AtomicInteger poolNumber = new AtomicInteger(1); - final ThreadGroup group; - final AtomicInteger threadNumber = new AtomicInteger(1); - final String namePrefix; + private static final AtomicInteger poolNumber = new AtomicInteger(1); + private final ThreadGroup group; + private final AtomicInteger threadNumber = new AtomicInteger(1); + private final String namePrefix; DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); group = (s != null)? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); - namePrefix = "pool-" + - poolNumber.getAndIncrement() + + namePrefix = "pool-" + + poolNumber.getAndIncrement() + "-thread-"; } public Thread newThread(Runnable r) { - Thread t = new Thread(group, r, + Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) @@ -553,38 +552,46 @@ public class Executors { } /** - * Thread factory capturing access control and class loader + * Thread factory capturing access control context and class loader */ static class PrivilegedThreadFactory extends DefaultThreadFactory { - private final ClassLoader ccl; private final AccessControlContext acc; + private final ClassLoader ccl; PrivilegedThreadFactory() { super(); - this.ccl = Thread.currentThread().getContextClassLoader(); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + // Calls to getContextClassLoader from this class + // never trigger a security check, but we check + // whether our callers have this permission anyways. + sm.checkPermission(new RuntimePermission("getContextClassLoader")); + + // Fail fast + sm.checkPermission(new RuntimePermission("setContextClassLoader")); + } this.acc = AccessController.getContext(); - acc.checkPermission(new RuntimePermission("setContextClassLoader")); + this.ccl = Thread.currentThread().getContextClassLoader(); } - + public Thread newThread(final Runnable r) { return super.newThread(new Runnable() { public void run() { - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { + AccessController.doPrivileged(new PrivilegedAction<Void>() { + public Void run() { Thread.currentThread().setContextClassLoader(ccl); r.run(); - return null; + return null; } }, acc); } }); } - } - /** + /** * A wrapper class that exposes only the ExecutorService methods - * of an implementation. + * of an ExecutorService implementation. */ static class DelegatedExecutorService extends AbstractExecutorService { private final ExecutorService e; @@ -611,8 +618,8 @@ public class Executors { throws InterruptedException { return e.invokeAll(tasks); } - public <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks, - long timeout, TimeUnit unit) + public <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks, + long timeout, TimeUnit unit) throws InterruptedException { return e.invokeAll(tasks, timeout, unit); } @@ -620,19 +627,29 @@ public class Executors { throws InterruptedException, ExecutionException { return e.invokeAny(tasks); } - public <T> T invokeAny(Collection<Callable<T>> tasks, - long timeout, TimeUnit unit) + public <T> T invokeAny(Collection<Callable<T>> tasks, + long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return e.invokeAny(tasks, timeout, unit); } } - + + static class FinalizableDelegatedExecutorService + extends DelegatedExecutorService { + FinalizableDelegatedExecutorService(ExecutorService executor) { + super(executor); + } + protected void finalize() { + super.shutdown(); + } + } + /** - * A wrapper class that exposes only the ExecutorService and - * ScheduleExecutor methods of a ScheduledExecutorService implementation. + * A wrapper class that exposes only the ScheduledExecutorService + * methods of a ScheduledExecutorService implementation. */ static class DelegatedScheduledExecutorService - extends DelegatedExecutorService + extends DelegatedExecutorService implements ScheduledExecutorService { private final ScheduledExecutorService e; DelegatedScheduledExecutorService(ScheduledExecutorService executor) { @@ -653,7 +670,7 @@ public class Executors { } } - + /** Cannot instantiate. */ private Executors() {} } diff --git a/concurrent/src/main/java/java/util/concurrent/Future.java b/concurrent/src/main/java/java/util/concurrent/Future.java index 38fa8c6..0459ee4 100644 --- a/concurrent/src/main/java/java/util/concurrent/Future.java +++ b/concurrent/src/main/java/java/util/concurrent/Future.java @@ -29,10 +29,13 @@ package java.util.concurrent; * class App { * ExecutorService executor = ... * ArchiveSearcher searcher = ... - * void showSearch(final String target) throws InterruptedException { - * Future<String> future = executor.submit(new Callable<String>() { - * public String call() { return searcher.search(target); } - * }); + * void showSearch(final String target) + * throws InterruptedException { + * Future<String> future + * = executor.submit(new Callable<String>() { + * public String call() { + * return searcher.search(target); + * }}); * displayOtherThings(); // do other things while searching * try { * displayText(future.get()); // use future @@ -41,8 +44,8 @@ package java.util.concurrent; * } * </pre> * - * The {@link FutureTask} class is an implementation of <tt>Future</tt> that - * implements <tt>Runnable</tt>, and so may be executed by an <tt>Executor</tt>. + * The {@link FutureTask} class is an implementation of <tt>Future</tt> that + * implements <tt>Runnable</tt>, and so may be executed by an <tt>Executor</tt>. * For example, the above construction with <tt>submit</tt> could be replaced by: * <pre> * FutureTask<String> future = @@ -52,6 +55,11 @@ package java.util.concurrent; * }}); * executor.execute(future); * </pre> + * + * <p>Memory consistency effects: Actions taken by the asynchronous computation + * <a href="package-summary.html#MemoryVisibility"> <i>happen-before</i></a> + * actions following the corresponding {@code Future.get()} in another thread. + * * @see FutureTask * @see Executor * @since 1.5 @@ -62,7 +70,7 @@ public interface Future<V> { /** * Attempts to cancel execution of this task. This attempt will - * fail if the task has already completed, already been cancelled, + * fail if the task has already completed, has already been cancelled, * or could not be cancelled for some other reason. If successful, * and this task has not started when <tt>cancel</tt> is called, * this task should never run. If the task has already started, @@ -70,6 +78,10 @@ public interface Future<V> { * whether the thread executing this task should be interrupted in * an attempt to stop the task. * + * <p>After this method returns, subsequent calls to {@link #isDone} will + * always return <tt>true</tt>. Subsequent calls to {@link #isCancelled} + * will always return <tt>true</tt> if this method returned <tt>true</tt>. + * * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this * task should be interrupted; otherwise, in-progress tasks are allowed * to complete @@ -83,18 +95,18 @@ public interface Future<V> { * Returns <tt>true</tt> if this task was cancelled before it completed * normally. * - * @return <tt>true</tt> if task was cancelled before it completed + * @return <tt>true</tt> if this task was cancelled before it completed */ boolean isCancelled(); /** - * Returns <tt>true</tt> if this task completed. + * Returns <tt>true</tt> if this task completed. * * Completion may be due to normal termination, an exception, or * cancellation -- in all of these cases, this method will return * <tt>true</tt>. - * - * @return <tt>true</tt> if this task completed. + * + * @return <tt>true</tt> if this task completed */ boolean isDone(); @@ -128,6 +140,3 @@ public interface Future<V> { V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; } - - - diff --git a/concurrent/src/main/java/java/util/concurrent/FutureTask.java b/concurrent/src/main/java/java/util/concurrent/FutureTask.java index 87db1cd..8aa9dd1 100644 --- a/concurrent/src/main/java/java/util/concurrent/FutureTask.java +++ b/concurrent/src/main/java/java/util/concurrent/FutureTask.java @@ -35,7 +35,7 @@ public class FutureTask<V> implements Future<V>, Runnable { private final Sync sync; /** - * Creates a <tt>FutureTask</tt> that will upon running, execute the + * Creates a <tt>FutureTask</tt> that will, upon running, execute the * given <tt>Callable</tt>. * * @param callable the callable task @@ -48,11 +48,11 @@ public class FutureTask<V> implements Future<V>, Runnable { } /** - * Creates a <tt>FutureTask</tt> that will upon running, execute the + * Creates a <tt>FutureTask</tt> that will, upon running, execute the * given <tt>Runnable</tt>, and arrange that <tt>get</tt> will return the * given result on successful completion. * - * @param runnable the runnable task + * @param runnable the runnable task * @param result the result to return on successful completion. If * you don't need a particular result, consider using * constructions of the form: @@ -66,7 +66,7 @@ public class FutureTask<V> implements Future<V>, Runnable { public boolean isCancelled() { return sync.innerIsCancelled(); } - + public boolean isDone() { return sync.innerIsDone(); } @@ -74,11 +74,17 @@ public class FutureTask<V> implements Future<V>, Runnable { public boolean cancel(boolean mayInterruptIfRunning) { return sync.innerCancel(mayInterruptIfRunning); } - + + /** + * @throws CancellationException {@inheritDoc} + */ public V get() throws InterruptedException, ExecutionException { return sync.innerGet(); } + /** + * @throws CancellationException {@inheritDoc} + */ public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return sync.innerGet(unit.toNanos(timeout)); @@ -98,8 +104,10 @@ public class FutureTask<V> implements Future<V>, Runnable { /** * Sets the result of this Future to the given value unless * this future has already been set or has been cancelled. + * This method is invoked internally by the <tt>run</tt> method + * upon successful completion of the computation. * @param v the value - */ + */ protected void set(V v) { sync.innerSet(v); } @@ -108,15 +116,22 @@ public class FutureTask<V> implements Future<V>, Runnable { * Causes this future to report an <tt>ExecutionException</tt> * with the given throwable as its cause, unless this Future has * already been set or has been cancelled. - * @param t the cause of failure. - */ + * This method is invoked internally by the <tt>run</tt> method + * upon failure of the computation. + * @param t the cause of failure + */ protected void setException(Throwable t) { sync.innerSetException(t); } - + + // The following (duplicated) doc comment can be removed once + // + // 6270645: Javadoc comments should be inherited from most derived + // superinterface or superclass + // is fixed. /** - * Sets this Future to the result of computation unless - * it has been cancelled. + * Sets this Future to the result of its computation + * unless it has been cancelled. */ public void run() { sync.innerRun(); @@ -143,6 +158,10 @@ public class FutureTask<V> implements Future<V>, Runnable { * Uses AQS sync state to represent run status */ private final class Sync extends AbstractQueuedSynchronizer { + private static final long serialVersionUID = -7828117401763700385L; + + /** State value representing that task is ready to run */ + private static final int READY = 0; /** State value representing that task is running */ private static final int RUNNING = 1; /** State value representing that task ran */ @@ -157,10 +176,10 @@ public class FutureTask<V> implements Future<V>, Runnable { /** The exception to throw from get() */ private Throwable exception; - /** + /** * The thread running task. When nulled after set/cancel, this * indicates that the results are accessible. Must be - * volatile, to serve as write barrier on completion. + * volatile, to ensure visibility upon completion. */ private volatile Thread runner; @@ -176,7 +195,7 @@ public class FutureTask<V> implements Future<V>, Runnable { * Implements AQS base acquire to succeed if ran or cancelled */ protected int tryAcquireShared(int ignore) { - return innerIsDone()? 1 : -1; + return innerIsDone() ? 1 : -1; } /** @@ -185,13 +204,13 @@ public class FutureTask<V> implements Future<V>, Runnable { */ protected boolean tryReleaseShared(int ignore) { runner = null; - return true; + return true; } boolean innerIsCancelled() { return getState() == CANCELLED; } - + boolean innerIsDone() { return ranOrCancelled(getState()) && runner == null; } @@ -207,7 +226,7 @@ public class FutureTask<V> implements Future<V>, Runnable { V innerGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException { if (!tryAcquireSharedNanos(0, nanosTimeout)) - throw new TimeoutException(); + throw new TimeoutException(); if (getState() == CANCELLED) throw new CancellationException(); if (exception != null) @@ -216,28 +235,55 @@ public class FutureTask<V> implements Future<V>, Runnable { } void innerSet(V v) { - int s = getState(); - if (ranOrCancelled(s) || !compareAndSetState(s, RAN)) - return; - result = v; - releaseShared(0); - done(); + for (;;) { + int s = getState(); + if (s == RAN) + return; + if (s == CANCELLED) { + // aggressively release to set runner to null, + // in case we are racing with a cancel request + // that will try to interrupt runner + releaseShared(0); + return; + } + if (compareAndSetState(s, RAN)) { + result = v; + releaseShared(0); + done(); + return; + } + } } void innerSetException(Throwable t) { - int s = getState(); - if (ranOrCancelled(s) || !compareAndSetState(s, RAN)) - return; - exception = t; - result = null; - releaseShared(0); - done(); + for (;;) { + int s = getState(); + if (s == RAN) + return; + if (s == CANCELLED) { + // aggressively release to set runner to null, + // in case we are racing with a cancel request + // that will try to interrupt runner + releaseShared(0); + return; + } + if (compareAndSetState(s, RAN)) { + exception = t; + releaseShared(0); + done(); + return; + } + } } boolean innerCancel(boolean mayInterruptIfRunning) { - int s = getState(); - if (ranOrCancelled(s) || !compareAndSetState(s, CANCELLED)) - return false; + for (;;) { + int s = getState(); + if (ranOrCancelled(s)) + return false; + if (compareAndSetState(s, CANCELLED)) + break; + } if (mayInterruptIfRunning) { Thread r = runner; if (r != null) @@ -249,28 +295,37 @@ public class FutureTask<V> implements Future<V>, Runnable { } void innerRun() { - if (!compareAndSetState(0, RUNNING)) + if (!compareAndSetState(READY, RUNNING)) return; - try { - runner = Thread.currentThread(); - innerSet(callable.call()); - } catch(Throwable ex) { - innerSetException(ex); - } + + runner = Thread.currentThread(); + if (getState() == RUNNING) { // recheck after setting thread + V result; + try { + result = callable.call(); + } catch (Throwable ex) { + setException(ex); + return; + } + set(result); + } else { + releaseShared(0); // cancel + } } boolean innerRunAndReset() { - if (!compareAndSetState(0, RUNNING)) + if (!compareAndSetState(READY, RUNNING)) return false; try { runner = Thread.currentThread(); - callable.call(); // don't set result + if (getState() == RUNNING) + callable.call(); // don't set result runner = null; - return compareAndSetState(RUNNING, 0); - } catch(Throwable ex) { - innerSetException(ex); + return compareAndSetState(RUNNING, READY); + } catch (Throwable ex) { + setException(ex); return false; - } + } } } } diff --git a/concurrent/src/main/java/java/util/concurrent/Java6Arrays.java b/concurrent/src/main/java/java/util/concurrent/Java6Arrays.java new file mode 100644 index 0000000..6b728be --- /dev/null +++ b/concurrent/src/main/java/java/util/concurrent/Java6Arrays.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.util.concurrent; + +import java.lang.reflect.Array; + +/** + * Arrays.copyOf and Arrays.copyOfRange backported from Java 6. + */ +class Java6Arrays { + + static <T> T[] copyOf(T[] original, int newLength) { + if (null == original) { + throw new NullPointerException(); + } + if (0 <= newLength) { + return copyOfRange(original, 0, newLength); + } + throw new NegativeArraySizeException(); + } + + static <T, U> T[] copyOf(U[] original, int newLength, + Class<? extends T[]> newType) { + if (0 <= newLength) { + return copyOfRange(original, 0, newLength, newType); + } + throw new NegativeArraySizeException(); + } + + @SuppressWarnings("unchecked") + static <T> T[] copyOfRange(T[] original, int start, int end) { + if (original.length >= start && 0 <= start) { + if (start <= end) { + int length = end - start; + int copyLength = Math.min(length, original.length - start); + T[] copy = (T[]) Array.newInstance(original.getClass().getComponentType(), length); + System.arraycopy(original, start, copy, 0, copyLength); + return copy; + } + throw new IllegalArgumentException(); + } + throw new ArrayIndexOutOfBoundsException(); + } + + @SuppressWarnings("unchecked") + static <T, U> T[] copyOfRange(U[] original, int start, int end, + Class<? extends T[]> newType) { + if (start <= end) { + if (original.length >= start && 0 <= start) { + int length = end - start; + int copyLength = Math.min(length, original.length - start); + T[] copy = (T[]) Array.newInstance(newType.getComponentType(), + length); + System.arraycopy(original, start, copy, 0, copyLength); + return copy; + } + throw new ArrayIndexOutOfBoundsException(); + } + throw new IllegalArgumentException(); + } + +} diff --git a/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java b/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java index 662f63e..e06f7bd 100644 --- a/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java +++ b/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java @@ -32,14 +32,15 @@ import java.util.*; * dynamically created upon each insertion unless this would bring the * queue above capacity. * - * <p>This class implements all of the <em>optional</em> methods - * of the {@link Collection} and {@link Iterator} interfaces. + * <p>This class and its iterator implement all of the + * <em>optional</em> methods of the {@link Collection} and {@link + * Iterator} interfaces. * * @since 1.5 * @author Doug Lea * @param <E> the type of elements held in this collection * - **/ + */ public class LinkedBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable { private static final long serialVersionUID = -6903933977591709194L; @@ -56,7 +57,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> * items have been entered since the signal. And symmetrically for * takes signalling puts. Operations such as remove(Object) and * iterators acquire both locks. - */ + */ /** * Linked list node class @@ -93,7 +94,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> private final Condition notFull = putLock.newCondition(); /** - * Signal a waiting take. Called only from put/offer (which do not + * Signals a waiting take. Called only from put/offer (which do not * otherwise ordinarily lock takeLock.) */ private void signalNotEmpty() { @@ -107,7 +108,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } /** - * Signal a waiting put. Called only from take/poll. + * Signals a waiting put. Called only from take/poll. */ private void signalNotFull() { final ReentrantLock putLock = this.putLock; @@ -120,7 +121,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } /** - * Create a node and link it at end of queue + * Creates a node and links it at end of queue. * @param x the item */ private void insert(E x) { @@ -128,11 +129,13 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } /** - * Remove a node from head of queue, + * Removes a node from head of queue, * @return the node */ private E extract() { - Node<E> first = head.next; + Node<E> h = head; + Node<E> first = h.next; + h.next = null; // help GC head = first; E x = first.item; first.item = null; @@ -167,9 +170,9 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> /** * Creates a <tt>LinkedBlockingQueue</tt> with the given (fixed) capacity. * - * @param capacity the capacity of this queue. + * @param capacity the capacity of this queue * @throws IllegalArgumentException if <tt>capacity</tt> is not greater - * than zero. + * than zero */ public LinkedBlockingQueue(int capacity) { if (capacity <= 0) throw new IllegalArgumentException(); @@ -182,14 +185,15 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> * {@link Integer#MAX_VALUE}, initially containing the elements of the * given collection, * added in traversal order of the collection's iterator. + * * @param c the collection of elements to initially contain - * @throws NullPointerException if <tt>c</tt> or any element within it - * is <tt>null</tt> + * @throws NullPointerException if the specified collection or any + * of its elements are null */ public LinkedBlockingQueue(Collection<? extends E> c) { this(Integer.MAX_VALUE); - for (Iterator<? extends E> it = c.iterator(); it.hasNext();) - add(it.next()); + for (E e : c) + add(e); } @@ -198,7 +202,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> /** * Returns the number of elements in this queue. * - * @return the number of elements in this queue. + * @return the number of elements in this queue */ public int size() { return count.get(); @@ -207,31 +211,29 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> // this doc comment is a modified copy of the inherited doc comment, // without the reference to unlimited queues. /** - * Returns the number of elements that this queue can ideally (in - * the absence of memory or resource constraints) accept without + * Returns the number of additional elements that this queue can ideally + * (in the absence of memory or resource constraints) accept without * blocking. This is always equal to the initial capacity of this queue * less the current <tt>size</tt> of this queue. - * <p>Note that you <em>cannot</em> always tell if - * an attempt to <tt>add</tt> an element will succeed by - * inspecting <tt>remainingCapacity</tt> because it may be the - * case that a waiting consumer is ready to <tt>take</tt> an - * element out of an otherwise full queue. - * - * @return the remaining capacity + * + * <p>Note that you <em>cannot</em> always tell if an attempt to insert + * an element will succeed by inspecting <tt>remainingCapacity</tt> + * because it may be the case that another thread is about to + * insert or remove an element. */ public int remainingCapacity() { return capacity - count.get(); } /** - * Adds the specified element to the tail of this queue, waiting if + * Inserts the specified element at the tail of this queue, waiting if * necessary for space to become available. - * @param o the element to add - * @throws InterruptedException if interrupted while waiting. - * @throws NullPointerException if the specified element is <tt>null</tt>. + * + * @throws InterruptedException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} */ - public void put(E o) throws InterruptedException { - if (o == null) throw new NullPointerException(); + public void put(E e) throws InterruptedException { + if (e == null) throw new NullPointerException(); // Note: convention in all put/take/etc is to preset // local var holding count negative to indicate failure unless set. int c = -1; @@ -255,7 +257,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> notFull.signal(); // propagate to a non-interrupted thread throw ie; } - insert(o); + insert(e); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); @@ -269,20 +271,16 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> /** * Inserts the specified element at the tail of this queue, waiting if * necessary up to the specified wait time for space to become available. - * @param o the element to add - * @param timeout how long to wait before giving up, in units of - * <tt>unit</tt> - * @param unit a <tt>TimeUnit</tt> determining how to interpret the - * <tt>timeout</tt> parameter + * * @return <tt>true</tt> if successful, or <tt>false</tt> if - * the specified waiting time elapses before space is available. - * @throws InterruptedException if interrupted while waiting. - * @throws NullPointerException if the specified element is <tt>null</tt>. + * the specified waiting time elapses before space is available. + * @throws InterruptedException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} */ - public boolean offer(E o, long timeout, TimeUnit unit) + public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { - if (o == null) throw new NullPointerException(); + if (e == null) throw new NullPointerException(); long nanos = unit.toNanos(timeout); int c = -1; final ReentrantLock putLock = this.putLock; @@ -291,7 +289,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> try { for (;;) { if (count.get() < capacity) { - insert(o); + insert(e); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); @@ -315,16 +313,18 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } /** - * Inserts the specified element at the tail of this queue if possible, - * returning immediately if this queue is full. + * Inserts the specified element at the tail of this queue if it is + * possible to do so immediately without exceeding the queue's capacity, + * returning <tt>true</tt> upon success and <tt>false</tt> if this queue + * is full. + * When using a capacity-restricted queue, this method is generally + * preferable to method {@link BlockingQueue#add add}, which can fail to + * insert an element only by throwing an exception. * - * @param o the element to add. - * @return <tt>true</tt> if it was possible to add the element to - * this queue, else <tt>false</tt> - * @throws NullPointerException if the specified element is <tt>null</tt> + * @throws NullPointerException if the specified element is null */ - public boolean offer(E o) { - if (o == null) throw new NullPointerException(); + public boolean offer(E e) { + if (e == null) throw new NullPointerException(); final AtomicInteger count = this.count; if (count.get() == capacity) return false; @@ -333,7 +333,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> putLock.lock(); try { if (count.get() < capacity) { - insert(o); + insert(e); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); @@ -447,6 +447,17 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } } + /** + * Removes a single instance of the specified element from this queue, + * if it is present. More formally, removes an element <tt>e</tt> such + * that <tt>o.equals(e)</tt>, if this queue contains one or more such + * elements. + * Returns <tt>true</tt> if this queue contained the specified element + * (or equivalently, if this queue changed as a result of the call). + * + * @param o element to be removed from this queue, if present + * @return <tt>true</tt> if this queue changed as a result of the call + */ public boolean remove(Object o) { if (o == null) return false; boolean removed = false; @@ -465,6 +476,8 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> if (removed) { p.item = null; trail.next = p.next; + if (last == p) + last = trail; if (count.getAndDecrement() == capacity) notFull.signalAll(); } @@ -474,6 +487,19 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> return removed; } + /** + * Returns an array containing all of the elements in this queue, in + * proper sequence. + * + * <p>The returned array will be "safe" in that no references to it are + * maintained by this queue. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + * <p>This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this queue + */ public Object[] toArray() { fullyLock(); try { @@ -488,6 +514,42 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } } + /** + * Returns an array containing all of the elements in this queue, in + * proper sequence; the runtime type of the returned array is that of + * the specified array. If the queue fits in the specified array, it + * is returned therein. Otherwise, a new array is allocated with the + * runtime type of the specified array and the size of this queue. + * + * <p>If this queue fits in the specified array with room to spare + * (i.e., the array has more elements than this queue), the element in + * the array immediately following the end of the queue is set to + * <tt>null</tt>. + * + * <p>Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + * <p>Suppose <tt>x</tt> is a queue known to contain only strings. + * The following code can be used to dump the queue into a newly + * allocated array of <tt>String</tt>: + * + * <pre> + * String[] y = x.toArray(new String[0]);</pre> + * + * Note that <tt>toArray(new Object[0])</tt> is identical in function to + * <tt>toArray()</tt>. + * + * @param a the array into which the elements of the queue are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose + * @return an array containing all of the elements in this queue + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this queue + * @throws NullPointerException if the specified array is null + */ public <T> T[] toArray(T[] a) { fullyLock(); try { @@ -499,6 +561,8 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> int k = 0; for (Node p = head.next; p != null; p = p.next) a[k++] = (T)p.item; + if (a.length > k) + a[k] = null; return a; } finally { fullyUnlock(); @@ -514,10 +578,16 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } } + /** + * Atomically removes all of the elements from this queue. + * The queue will be empty after this call returns. + */ public void clear() { fullyLock(); try { head.next = null; + assert head.item == null; + last = head; if (count.getAndSet(0) == capacity) notFull.signalAll(); } finally { @@ -525,16 +595,24 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } } + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection<? super E> c) { if (c == null) throw new NullPointerException(); if (c == this) throw new IllegalArgumentException(); - Node first; + Node<E> first; fullyLock(); try { first = head.next; head.next = null; + assert head.item == null; + last = head; if (count.getAndSet(0) == capacity) notFull.signalAll(); } finally { @@ -549,14 +627,18 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } return n; } - + + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection<? super E> c, int maxElements) { if (c == null) throw new NullPointerException(); if (c == this) throw new IllegalArgumentException(); - if (maxElements <= 0) - return 0; fullyLock(); try { int n = 0; @@ -569,6 +651,9 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } if (n != 0) { head.next = p; + assert head.item == null; + if (p == null) + last = head; if (count.getAndAdd(-n) == capacity) notFull.signalAll(); } @@ -581,12 +666,12 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> /** * Returns an iterator over the elements in this queue in proper sequence. * The returned <tt>Iterator</tt> is a "weakly consistent" iterator that - * will never throw {@link java.util.ConcurrentModificationException}, + * will never throw {@link ConcurrentModificationException}, * and guarantees to traverse elements as they existed upon * construction of the iterator, and may (but is not guaranteed to) * reflect any modifications subsequent to construction. * - * @return an iterator over the elements in this queue in proper sequence. + * @return an iterator over the elements in this queue in proper sequence */ public Iterator<E> iterator() { return new Itr(); @@ -660,6 +745,8 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> if (p == node) { p.item = null; trail.next = p.next; + if (last == p) + last = trail; int c = count.getAndDecrement(); if (c == capacity) notFull.signalAll(); diff --git a/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java b/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java index 83f1040..7a33dc7 100644 --- a/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java +++ b/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java @@ -19,16 +19,47 @@ import java.util.*; * blocking retrieval operations. While this queue is logically * unbounded, attempted additions may fail due to resource exhaustion * (causing <tt>OutOfMemoryError</tt>). This class does not permit - * <tt>null</tt> elements. A priority queue relying on natural - * ordering also does not permit insertion of non-comparable objects - * (doing so results in <tt>ClassCastException</tt>). + * <tt>null</tt> elements. A priority queue relying on {@linkplain + * Comparable natural ordering} also does not permit insertion of + * non-comparable objects (doing so results in + * <tt>ClassCastException</tt>). * - * <p>This class implements all of the <em>optional</em> methods - * of the {@link Collection} and {@link Iterator} interfaces. - * <p>The Iterator provided in method {@link #iterator()} is - * <em>not</em> guaranteed to traverse the elements of the - * PriorityBlockingQueue in any particular order. If you need ordered - * traversal, consider using <tt>Arrays.sort(pq.toArray())</tt>. + * <p>This class and its iterator implement all of the + * <em>optional</em> methods of the {@link Collection} and {@link + * Iterator} interfaces. The Iterator provided in method {@link + * #iterator()} is <em>not</em> guaranteed to traverse the elements of + * the PriorityBlockingQueue in any particular order. If you need + * ordered traversal, consider using + * <tt>Arrays.sort(pq.toArray())</tt>. Also, method <tt>drainTo</tt> + * can be used to <em>remove</em> some or all elements in priority + * order and place them in another collection. + * + * <p>Operations on this class make no guarantees about the ordering + * of elements with equal priority. If you need to enforce an + * ordering, you can define custom classes or comparators that use a + * secondary key to break ties in primary priority values. For + * example, here is a class that applies first-in-first-out + * tie-breaking to comparable elements. To use it, you would insert a + * <tt>new FIFOEntry(anEntry)</tt> instead of a plain entry object. + * + * <pre> + * class FIFOEntry<E extends Comparable<? super E>> + * implements Comparable<FIFOEntry<E>> { + * final static AtomicLong seq = new AtomicLong(); + * final long seqNum; + * final E entry; + * public FIFOEntry(E entry) { + * seqNum = seq.getAndIncrement(); + * this.entry = entry; + * } + * public E getEntry() { return entry; } + * public int compareTo(FIFOEntry<E> other) { + * int res = entry.compareTo(other.entry); + * if (res == 0 && other.entry != this.entry) + * res = (seqNum < other.seqNum ? -1 : 1); + * return res; + * } + * }</pre> * * @since 1.5 * @author Doug Lea @@ -43,24 +74,22 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> private final Condition notEmpty = lock.newCondition(); /** - * Creates a <tt>PriorityBlockingQueue</tt> with the default initial - * capacity - * (11) that orders its elements according to their natural - * ordering (using <tt>Comparable</tt>). + * Creates a <tt>PriorityBlockingQueue</tt> with the default + * initial capacity (11) that orders its elements according to + * their {@linkplain Comparable natural ordering}. */ public PriorityBlockingQueue() { q = new PriorityQueue<E>(); } /** - * Creates a <tt>PriorityBlockingQueue</tt> with the specified initial - * capacity - * that orders its elements according to their natural ordering - * (using <tt>Comparable</tt>). + * Creates a <tt>PriorityBlockingQueue</tt> with the specified + * initial capacity that orders its elements according to their + * {@linkplain Comparable natural ordering}. * - * @param initialCapacity the initial capacity for this priority queue. + * @param initialCapacity the initial capacity for this priority queue * @throws IllegalArgumentException if <tt>initialCapacity</tt> is less - * than 1 + * than 1 */ public PriorityBlockingQueue(int initialCapacity) { q = new PriorityQueue<E>(initialCapacity, null); @@ -68,15 +97,15 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> /** * Creates a <tt>PriorityBlockingQueue</tt> with the specified initial - * capacity - * that orders its elements according to the specified comparator. + * capacity that orders its elements according to the specified + * comparator. * - * @param initialCapacity the initial capacity for this priority queue. - * @param comparator the comparator used to order this priority queue. - * If <tt>null</tt> then the order depends on the elements' natural - * ordering. + * @param initialCapacity the initial capacity for this priority queue + * @param comparator the comparator that will be used to order this + * priority queue. If {@code null}, the {@linkplain Comparable + * natural ordering} of the elements will be used. * @throws IllegalArgumentException if <tt>initialCapacity</tt> is less - * than 1 + * than 1 */ public PriorityBlockingQueue(int initialCapacity, Comparator<? super E> comparator) { @@ -85,75 +114,54 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> /** * Creates a <tt>PriorityBlockingQueue</tt> containing the elements - * in the specified collection. The priority queue has an initial - * capacity of 110% of the size of the specified collection. If - * the specified collection is a {@link SortedSet} or a {@link - * PriorityQueue}, this priority queue will be sorted according to - * the same comparator, or according to its elements' natural - * order if the collection is sorted according to its elements' - * natural order. Otherwise, this priority queue is ordered - * according to its elements' natural order. + * in the specified collection. If the specified collection is a + * {@link SortedSet} or a {@link PriorityQueue}, this + * priority queue will be ordered according to the same ordering. + * Otherwise, this priority queue will be ordered according to the + * {@linkplain Comparable natural ordering} of its elements. * - * @param c the collection whose elements are to be placed - * into this priority queue. + * @param c the collection whose elements are to be placed + * into this priority queue * @throws ClassCastException if elements of the specified collection * cannot be compared to one another according to the priority - * queue's ordering. - * @throws NullPointerException if <tt>c</tt> or any element within it - * is <tt>null</tt> + * queue's ordering + * @throws NullPointerException if the specified collection or any + * of its elements are null */ public PriorityBlockingQueue(Collection<? extends E> c) { q = new PriorityQueue<E>(c); } - - // these first few override just to update doc comments - /** - * Adds the specified element to this queue. - * @param o the element to add - * @return <tt>true</tt> (as per the general contract of - * <tt>Collection.add</tt>). + * Inserts the specified element into this priority queue. * - * @throws NullPointerException if the specified element is <tt>null</tt>. + * @param e the element to add + * @return <tt>true</tt> (as specified by {@link Collection#add}) * @throws ClassCastException if the specified element cannot be compared - * with elements currently in the priority queue according - * to the priority queue's ordering. + * with elements currently in the priority queue according to the + * priority queue's ordering + * @throws NullPointerException if the specified element is null */ - public boolean add(E o) { - return super.add(o); + public boolean add(E e) { + return offer(e); } // BEGIN android-changed /** - * Returns the comparator used to order this collection, or <tt>null</tt> - * if this collection is sorted according to its elements natural ordering - * (using <tt>Comparable</tt>). - * - * @return the comparator used to order this collection, or <tt>null</tt> - * if this collection is sorted according to its elements natural ordering. - */ - public Comparator<? super E> comparator() { - return q.comparator(); - } - // END android-changed - - /** * Inserts the specified element into this priority queue. * - * @param o the element to add - * @return <tt>true</tt> + * @param e the element to add + * @return <tt>true</tt> (as specified by {@link Queue#offer}) * @throws ClassCastException if the specified element cannot be compared - * with elements currently in the priority queue according - * to the priority queue's ordering. - * @throws NullPointerException if the specified element is <tt>null</tt>. + * with elements currently in the priority queue according to the + * priority queue's ordering + * @throws NullPointerException if the specified element is null */ - public boolean offer(E o) { - if (o == null) throw new NullPointerException(); + public boolean offer(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { - boolean ok = q.offer(o); + boolean ok = q.offer(e); assert ok; notEmpty.signal(); return true; @@ -163,32 +171,44 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> } /** - * Adds the specified element to this priority queue. As the queue is + * Inserts the specified element into this priority queue. As the queue is * unbounded this method will never block. - * @param o the element to add - * @throws ClassCastException if the element cannot be compared - * with elements currently in the priority queue according - * to the priority queue's ordering. - * @throws NullPointerException if the specified element is <tt>null</tt>. + * + * @param e the element to add + * @throws ClassCastException if the specified element cannot be compared + * with elements currently in the priority queue according to the + * priority queue's ordering + * @throws NullPointerException if the specified element is null */ - public void put(E o) { - offer(o); // never need to block + public void put(E e) { + offer(e); // never need to block } /** * Inserts the specified element into this priority queue. As the queue is * unbounded this method will never block. - * @param o the element to add + * + * @param e the element to add * @param timeout This parameter is ignored as the method never blocks * @param unit This parameter is ignored as the method never blocks * @return <tt>true</tt> - * @throws ClassCastException if the element cannot be compared - * with elements currently in the priority queue according - * to the priority queue's ordering. - * @throws NullPointerException if the specified element is <tt>null</tt>. + * @throws ClassCastException if the specified element cannot be compared + * with elements currently in the priority queue according to the + * priority queue's ordering + * @throws NullPointerException if the specified element is null */ - public boolean offer(E o, long timeout, TimeUnit unit) { - return offer(o); // never need to block + public boolean offer(E e, long timeout, TimeUnit unit) { + return offer(e); // never need to block + } + + public E poll() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return q.poll(); + } finally { + lock.unlock(); + } } public E take() throws InterruptedException { @@ -210,17 +230,6 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> } } - - public E poll() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - return q.poll(); - } finally { - lock.unlock(); - } - } - public E poll(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; @@ -254,6 +263,19 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> } } + /** + * Returns the comparator used to order the elements in this queue, + * or <tt>null</tt> if this queue uses the {@linkplain Comparable + * natural ordering} of its elements. + * + * @return the comparator used to order the elements in this queue, + * or <tt>null</tt> if this queue uses the natural + * ordering of its elements + */ + public Comparator<? super E> comparator() { + return q.comparator(); + } + public int size() { final ReentrantLock lock = this.lock; lock.lock(); @@ -273,6 +295,17 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> return Integer.MAX_VALUE; } + /** + * Removes a single instance of the specified element from this queue, + * if it is present. More formally, removes an element {@code e} such + * that {@code o.equals(e)}, if this queue contains one or more such + * elements. Returns {@code true} if and only if this queue contained + * the specified element (or equivalently, if this queue changed as a + * result of the call). + * + * @param o element to be removed from this queue, if present + * @return <tt>true</tt> if this queue changed as a result of the call + */ public boolean remove(Object o) { final ReentrantLock lock = this.lock; lock.lock(); @@ -283,6 +316,14 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> } } + /** + * Returns {@code true} if this queue contains the specified element. + * More formally, returns {@code true} if and only if this queue contains + * at least one element {@code e} such that {@code o.equals(e)}. + * + * @param o object to be checked for containment in this queue + * @return <tt>true</tt> if this queue contains the specified element + */ public boolean contains(Object o) { final ReentrantLock lock = this.lock; lock.lock(); @@ -293,6 +334,19 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> } } + /** + * Returns an array containing all of the elements in this queue. + * The returned array elements are in no particular order. + * + * <p>The returned array will be "safe" in that no references to it are + * maintained by this queue. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + * <p>This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this queue + */ public Object[] toArray() { final ReentrantLock lock = this.lock; lock.lock(); @@ -314,6 +368,12 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> } } + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection<? super E> c) { if (c == null) throw new NullPointerException(); @@ -334,6 +394,12 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> } } + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection<? super E> c, int maxElements) { if (c == null) throw new NullPointerException(); @@ -357,7 +423,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> } /** - * Atomically removes all of the elements from this delay queue. + * Atomically removes all of the elements from this queue. * The queue will be empty after this call returns. */ public void clear() { @@ -370,6 +436,43 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> } } + /** + * Returns an array containing all of the elements in this queue; the + * runtime type of the returned array is that of the specified array. + * The returned array elements are in no particular order. + * If the queue fits in the specified array, it is returned therein. + * Otherwise, a new array is allocated with the runtime type of the + * specified array and the size of this queue. + * + * <p>If this queue fits in the specified array with room to spare + * (i.e., the array has more elements than this queue), the element in + * the array immediately following the end of the queue is set to + * <tt>null</tt>. + * + * <p>Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + * <p>Suppose <tt>x</tt> is a queue known to contain only strings. + * The following code can be used to dump the queue into a newly + * allocated array of <tt>String</tt>: + * + * <pre> + * String[] y = x.toArray(new String[0]);</pre> + * + * Note that <tt>toArray(new Object[0])</tt> is identical in function to + * <tt>toArray()</tt>. + * + * @param a the array into which the elements of the queue are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose + * @return an array containing all of the elements in this queue + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this queue + * @throws NullPointerException if the specified array is null + */ public <T> T[] toArray(T[] a) { final ReentrantLock lock = this.lock; lock.lock(); @@ -383,54 +486,58 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> /** * Returns an iterator over the elements in this queue. The * iterator does not return the elements in any particular order. - * The returned iterator is a thread-safe "fast-fail" iterator - * that will throw {@link - * java.util.ConcurrentModificationException} upon detected - * interference. + * The returned <tt>Iterator</tt> is a "weakly consistent" + * iterator that will never throw {@link + * ConcurrentModificationException}, and guarantees to traverse + * elements as they existed upon construction of the iterator, and + * may (but is not guaranteed to) reflect any modifications + * subsequent to construction. * - * @return an iterator over the elements in this queue. + * @return an iterator over the elements in this queue */ public Iterator<E> iterator() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - return new Itr(q.iterator()); - } finally { - lock.unlock(); - } + return new Itr(toArray()); } - private class Itr<E> implements Iterator<E> { - private final Iterator<E> iter; - Itr(Iterator<E> i) { - iter = i; + /** + * Snapshot iterator that works off copy of underlying q array. + */ + private class Itr implements Iterator<E> { + final Object[] array; // Array of all elements + int cursor; // index of next element to return; + int lastRet; // index of last element, or -1 if no such + + Itr(Object[] array) { + lastRet = -1; + this.array = array; } public boolean hasNext() { - /* - * No sync -- we rely on underlying hasNext to be - * stateless, in which case we can return true by mistake - * only when next() will subsequently throw - * ConcurrentModificationException. - */ - return iter.hasNext(); + return cursor < array.length; } public E next() { - ReentrantLock lock = PriorityBlockingQueue.this.lock; - lock.lock(); - try { - return iter.next(); - } finally { - lock.unlock(); - } + if (cursor >= array.length) + throw new NoSuchElementException(); + lastRet = cursor; + return (E)array[cursor++]; } public void remove() { - ReentrantLock lock = PriorityBlockingQueue.this.lock; + if (lastRet < 0) + throw new IllegalStateException(); + Object x = array[lastRet]; + lastRet = -1; + // Traverse underlying queue to find == element, + // not just a .equals element. lock.lock(); try { - iter.remove(); + for (Iterator it = q.iterator(); it.hasNext(); ) { + if (it.next() == x) { + it.remove(); + return; + } + } } finally { lock.unlock(); } @@ -438,7 +545,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> } /** - * Save the state to a stream (that is, serialize it). This + * Saves the state to a stream (that is, serializes it). This * merely wraps default serialization within lock. The * serialization strategy for items is left to underlying * Queue. Note that locking is not needed on deserialization, so diff --git a/concurrent/src/main/java/java/util/concurrent/RejectedExecutionException.java b/concurrent/src/main/java/java/util/concurrent/RejectedExecutionException.java index 199b9de..30b043d 100644 --- a/concurrent/src/main/java/java/util/concurrent/RejectedExecutionException.java +++ b/concurrent/src/main/java/java/util/concurrent/RejectedExecutionException.java @@ -9,7 +9,7 @@ package java.util.concurrent; /** * Exception thrown by an {@link Executor} when a task cannot be * accepted for execution. - * + * * @since 1.5 * @author Doug Lea */ diff --git a/concurrent/src/main/java/java/util/concurrent/RejectedExecutionHandler.java b/concurrent/src/main/java/java/util/concurrent/RejectedExecutionHandler.java index 4b4bbea..417a27c 100644 --- a/concurrent/src/main/java/java/util/concurrent/RejectedExecutionHandler.java +++ b/concurrent/src/main/java/java/util/concurrent/RejectedExecutionHandler.java @@ -7,8 +7,7 @@ package java.util.concurrent; /** - * A handler for tasks that cannot be executed by a {@link - * ThreadPoolExecutor}. + * A handler for tasks that cannot be executed by a {@link ThreadPoolExecutor}. * * @since 1.5 * @author Doug Lea @@ -17,13 +16,14 @@ public interface RejectedExecutionHandler { /** * Method that may be invoked by a {@link ThreadPoolExecutor} when - * <tt>execute</tt> cannot accept a task. This may occur when no - * more threads or queue slots are available because their bounds - * would be exceeded, or upon shutdown of the Executor. + * {@link ThreadPoolExecutor#execute execute} cannot accept a + * task. This may occur when no more threads or queue slots are + * available because their bounds would be exceeded, or upon + * shutdown of the Executor. * - * In the absence other alternatives, the method may throw an - * unchecked {@link RejectedExecutionException}, which will be - * propagated to the caller of <tt>execute</tt>. + * <p>In the absence of other alternatives, the method may throw + * an unchecked {@link RejectedExecutionException}, which will be + * propagated to the caller of {@code execute}. * * @param r the runnable task requested to be executed * @param executor the executor attempting to execute this task diff --git a/concurrent/src/main/java/java/util/concurrent/ScheduledExecutorService.java b/concurrent/src/main/java/java/util/concurrent/ScheduledExecutorService.java index 9579fef..c170c4a 100644 --- a/concurrent/src/main/java/java/util/concurrent/ScheduledExecutorService.java +++ b/concurrent/src/main/java/java/util/concurrent/ScheduledExecutorService.java @@ -10,13 +10,13 @@ import java.util.*; /** * An {@link ExecutorService} that can schedule commands to run after a given - * delay, or to execute periodically. + * delay, or to execute periodically. * * <p> The <tt>schedule</tt> methods create tasks with various delays * and return a task object that can be used to cancel or check * execution. The <tt>scheduleAtFixedRate</tt> and * <tt>scheduleWithFixedDelay</tt> methods create and execute tasks - * that run periodically until cancelled. + * that run periodically until cancelled. * * <p> Commands submitted using the {@link Executor#execute} and * {@link ExecutorService} <tt>submit</tt> methods are scheduled with @@ -33,27 +33,27 @@ import java.util.*; * TimeUnit.MILLISECONDS)</tt>. Beware however that expiration of a * relative delay need not coincide with the current <tt>Date</tt> at * which the task is enabled due to network time synchronization - * protocols, clock drift, or other factors. + * protocols, clock drift, or other factors. * * The {@link Executors} class provides convenient factory methods for * the ScheduledExecutorService implementations provided in this package. * * <h3>Usage Example</h3> - * + * * Here is a class with a method that sets up a ScheduledExecutorService * to beep every ten seconds for an hour: * * <pre> - * import static java.util.concurrent.TimeUnit; + * import static java.util.concurrent.TimeUnit.*; * class BeeperControl { - * private final ScheduledExecutorService scheduler = + * private final ScheduledExecutorService scheduler = * Executors.newScheduledThreadPool(1); * * public void beepForAnHour() { * final Runnable beeper = new Runnable() { * public void run() { System.out.println("beep"); } * }; - * final ScheduledFuture<?> beeperHandle = + * final ScheduledFuture<?> beeperHandle = * scheduler.scheduleAtFixedRate(beeper, 10, 10, SECONDS); * scheduler.schedule(new Runnable() { * public void run() { beeperHandle.cancel(true); } @@ -70,76 +70,90 @@ public interface ScheduledExecutorService extends ExecutorService { /** * Creates and executes a one-shot action that becomes enabled * after the given delay. - * @param command the task to execute. - * @param delay the time from now to delay execution. - * @param unit the time unit of the delay parameter. - * @return a Future representing pending completion of the task, - * and whose <tt>get()</tt> method will return <tt>null</tt> - * upon completion. - * @throws RejectedExecutionException if task cannot be scheduled - * for execution. + * + * @param command the task to execute + * @param delay the time from now to delay execution + * @param unit the time unit of the delay parameter + * @return a ScheduledFuture representing pending completion of + * the task and whose <tt>get()</tt> method will return + * <tt>null</tt> upon completion + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution * @throws NullPointerException if command is null */ - public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit); + public ScheduledFuture<?> schedule(Runnable command, + long delay, TimeUnit unit); /** * Creates and executes a ScheduledFuture that becomes enabled after the * given delay. - * @param callable the function to execute. - * @param delay the time from now to delay execution. - * @param unit the time unit of the delay parameter. - * @return a ScheduledFuture that can be used to extract result or cancel. - * @throws RejectedExecutionException if task cannot be scheduled - * for execution. + * + * @param callable the function to execute + * @param delay the time from now to delay execution + * @param unit the time unit of the delay parameter + * @return a ScheduledFuture that can be used to extract result or cancel + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution * @throws NullPointerException if callable is null */ - public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit); + public <V> ScheduledFuture<V> schedule(Callable<V> callable, + long delay, TimeUnit unit); /** * Creates and executes a periodic action that becomes enabled first * after the given initial delay, and subsequently with the given * period; that is executions will commence after * <tt>initialDelay</tt> then <tt>initialDelay+period</tt>, then - * <tt>initialDelay + 2 * period</tt>, and so on. + * <tt>initialDelay + 2 * period</tt>, and so on. * If any execution of the task * encounters an exception, subsequent executions are suppressed. * Otherwise, the task will only terminate via cancellation or - * termination of the executor. - * @param command the task to execute. - * @param initialDelay the time to delay first execution. - * @param period the period between successive executions. + * termination of the executor. If any execution of this task + * takes longer than its period, then subsequent executions + * may start late, but will not concurrently execute. + * + * @param command the task to execute + * @param initialDelay the time to delay first execution + * @param period the period between successive executions * @param unit the time unit of the initialDelay and period parameters - * @return a Future representing pending completion of the task, - * and whose <tt>get()</tt> method will throw an exception upon - * cancellation. - * @throws RejectedExecutionException if task cannot be scheduled - * for execution. + * @return a ScheduledFuture representing pending completion of + * the task, and whose <tt>get()</tt> method will throw an + * exception upon cancellation + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution * @throws NullPointerException if command is null - * @throws IllegalArgumentException if period less than or equal to zero. + * @throws IllegalArgumentException if period less than or equal to zero */ - public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit); - + public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, + long initialDelay, + long period, + TimeUnit unit); + /** * Creates and executes a periodic action that becomes enabled first * after the given initial delay, and subsequently with the * given delay between the termination of one execution and the - * commencement of the next. If any execution of the task + * commencement of the next. If any execution of the task * encounters an exception, subsequent executions are suppressed. * Otherwise, the task will only terminate via cancellation or * termination of the executor. - * @param command the task to execute. - * @param initialDelay the time to delay first execution. + * + * @param command the task to execute + * @param initialDelay the time to delay first execution * @param delay the delay between the termination of one - * execution and the commencement of the next. + * execution and the commencement of the next * @param unit the time unit of the initialDelay and delay parameters - * @return a Future representing pending completion of the task, - * and whose <tt>get()</tt> method will throw an exception upon - * cancellation. - * @throws RejectedExecutionException if task cannot be scheduled - * for execution. + * @return a ScheduledFuture representing pending completion of + * the task, and whose <tt>get()</tt> method will throw an + * exception upon cancellation + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution * @throws NullPointerException if command is null - * @throws IllegalArgumentException if delay less than or equal to zero. + * @throws IllegalArgumentException if delay less than or equal to zero */ - public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit); + public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, + long initialDelay, + long delay, + TimeUnit unit); } diff --git a/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java b/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java index 09ec681..83ffb99 100644 --- a/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java +++ b/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java @@ -4,8 +4,13 @@ * http://creativecommons.org/licenses/publicdomain */ +/* + * Modified in Apache Harmony. + */ + package java.util.concurrent; import java.util.concurrent.atomic.*; +import java.util.concurrent.locks.*; import java.util.*; /** @@ -16,25 +21,60 @@ import java.util.*; * flexibility or capabilities of {@link ThreadPoolExecutor} (which * this class extends) are required. * - * <p> Delayed tasks execute no sooner than they are enabled, but + * <p>Delayed tasks execute no sooner than they are enabled, but * without any real-time guarantees about when, after they are * enabled, they will commence. Tasks scheduled for exactly the same * execution time are enabled in first-in-first-out (FIFO) order of * submission. * + * <p>Successive executions of a task scheduled via + * <code>scheduleAtFixedRate</code> or + * <code>scheduleWithFixedDelay</code> do not overlap. While different + * executions may be performed by different threads, the effects of + * prior executions <a + * href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> + * those of subsequent ones. + * * <p>While this class inherits from {@link ThreadPoolExecutor}, a few * of the inherited tuning methods are not useful for it. In - * particular, because it acts as a fixed-sized pool using - * <tt>corePoolSize</tt> threads and an unbounded queue, adjustments - * to <tt>maximumPoolSize</tt> have no useful effect. + * particular, because it acts as a fixed-sized pool using {@code + * corePoolSize} threads and an unbounded queue, adjustments to {@code + * maximumPoolSize} have no useful effect. * * @since 1.5 * @author Doug Lea */ -public class ScheduledThreadPoolExecutor - extends ThreadPoolExecutor +public class ScheduledThreadPoolExecutor + extends ThreadPoolExecutor implements ScheduledExecutorService { + /* + * This class specializes ThreadPoolExecutor implementation by + * + * 1. Using a custom task type, ScheduledFutureTask for + * tasks, even those that don't require scheduling (i.e., + * those submitted using ExecutorService execute, not + * ScheduledExecutorService methods) which are treated as + * delayed tasks with a delay of zero. + * + * 2. Using a custom queue (DelayedWorkQueue), a variant of + * unbounded DelayQueue. The lack of capacity constraint and + * the fact that corePoolSize and maximumPoolSize are + * effectively identical simplifies some execution mechanics + * (see delayedExecute) compared to ThreadPoolExecutor. + * + * 3. Supporting optional run-after-shutdown parameters, which + * leads to overrides of shutdown methods to remove and cancel + * tasks that should NOT be run after shutdown, as well as + * different recheck logic when task (re)submission overlaps + * with a shutdown. + * + * 4. Task decoration methods to allow interception and + * instrumentation, which are needed because subclasses cannot + * otherwise override submit methods to get this effect. These + * don't have any impact on pool control logic though. + */ + /** * False if should cancel/suppress periodic tasks on shutdown. */ @@ -46,28 +86,40 @@ public class ScheduledThreadPoolExecutor private volatile boolean executeExistingDelayedTasksAfterShutdown = true; /** + * True if ScheduledFutureTask.cancel should remove from queue + */ + private volatile boolean removeOnCancel = false; + + /** * Sequence number to break scheduling ties, and in turn to * guarantee FIFO order among tied entries. */ private static final AtomicLong sequencer = new AtomicLong(0); - /** Base of nanosecond timings, to avoid wrapping */ - private static final long NANO_ORIGIN = System.nanoTime(); /** - * Returns nanosecond time offset by origin + * Value of System.nanoTime upon static initialization. This is + * used as an offset by now() to avoid wraparound of time values + * that would make them appear negative. */ - final long now() { - return System.nanoTime() - NANO_ORIGIN; + static final long initialNanoTime = System.nanoTime(); + + /** + * Returns current nanosecond time. + */ + static long now() { + return System.nanoTime() - initialNanoTime; } - private class ScheduledFutureTask<V> + private class ScheduledFutureTask<V> extends FutureTask<V> implements ScheduledFuture<V> { - + /** Sequence number to break ties FIFO */ private final long sequenceNumber; + /** The time the task is enabled to execute in nanoTime units */ private long time; + /** * Period in nanoseconds for repeating tasks. A positive * value indicates fixed-rate execution. A negative value @@ -76,8 +128,16 @@ public class ScheduledThreadPoolExecutor */ private final long period; + /** The actual task to be re-enqueued by reExecutePeriodic */ + ScheduledFutureTask<V> outerTask = this; + /** - * Creates a one-shot action with given nanoTime-based trigger time + * Index into delay queue, to support faster cancellation. + */ + int heapIndex; + + /** + * Creates a one-shot action with given nanoTime-based trigger time. */ ScheduledFutureTask(Runnable r, V result, long ns) { super(r, result); @@ -87,9 +147,9 @@ public class ScheduledThreadPoolExecutor } /** - * Creates a periodic action with given nano time and period + * Creates a periodic action with given nano time and period. */ - ScheduledFutureTask(Runnable r, V result, long ns, long period) { + ScheduledFutureTask(Runnable r, V result, long ns, long period) { super(r, result); this.time = ns; this.period = period; @@ -97,7 +157,7 @@ public class ScheduledThreadPoolExecutor } /** - * Creates a one-shot action with given nanoTime-based trigger + * Creates a one-shot action with given nanoTime-based trigger. */ ScheduledFutureTask(Callable<V> callable, long ns) { super(callable); @@ -107,122 +167,162 @@ public class ScheduledThreadPoolExecutor } public long getDelay(TimeUnit unit) { - long d = unit.convert(time - now(), TimeUnit.NANOSECONDS); - return d; + long d = time - now(); + return d<=0? 0 : unit.convert(d, TimeUnit.NANOSECONDS); } public int compareTo(Delayed other) { if (other == this) // compare zero ONLY if same object return 0; - ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other; - long diff = time - x.time; - if (diff < 0) - return -1; - else if (diff > 0) - return 1; - else if (sequenceNumber < x.sequenceNumber) - return -1; - else - return 1; + if (other instanceof ScheduledFutureTask) { + ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other; + long diff = time - x.time; + if (diff < 0) + return -1; + else if (diff > 0) + return 1; + else if (sequenceNumber < x.sequenceNumber) + return -1; + else + return 1; + } + long d = (getDelay(TimeUnit.NANOSECONDS) - + other.getDelay(TimeUnit.NANOSECONDS)); + return (d == 0) ? 0 : ((d < 0) ? -1 : 1); } /** * Returns true if this is a periodic (not a one-shot) action. + * * @return true if periodic */ - boolean isPeriodic() { + public boolean isPeriodic() { return period != 0; } /** - * Run a periodic task + * Sets the next time to run for a periodic task. */ - private void runPeriodic() { - boolean ok = ScheduledFutureTask.super.runAndReset(); - boolean down = isShutdown(); - // Reschedule if not cancelled and not shutdown or policy allows - if (ok && (!down || - (getContinueExistingPeriodicTasksAfterShutdownPolicy() && - !isTerminating()))) { - long p = period; - if (p > 0) - time += p; - else - time = now() - p; - ScheduledThreadPoolExecutor.super.getQueue().add(this); - } - // This might have been the final executed delayed - // task. Wake up threads to check. - else if (down) - interruptIdleWorkers(); + private void setNextRunTime() { + long p = period; + if (p > 0) + time += p; + else + time = now() - p; + } + + public boolean cancel(boolean mayInterruptIfRunning) { + boolean cancelled = super.cancel(mayInterruptIfRunning); + if (cancelled && removeOnCancel && heapIndex >= 0) + remove(this); + return cancelled; } /** * Overrides FutureTask version so as to reset/requeue if periodic. - */ + */ public void run() { - if (isPeriodic()) - runPeriodic(); - else + boolean periodic = isPeriodic(); + if (!canRunInCurrentRunState(periodic)) + cancel(false); + else if (!periodic) ScheduledFutureTask.super.run(); + else if (ScheduledFutureTask.super.runAndReset()) { + setNextRunTime(); + reExecutePeriodic(outerTask); + } + } + } + + /** + * Returns true if can run a task given current run state + * and run-after-shutdown parameters. + * + * @param periodic true if this task periodic, false if delayed + */ + boolean canRunInCurrentRunState(boolean periodic) { + return isRunningOrShutdown(periodic ? + continueExistingPeriodicTasksAfterShutdown : + executeExistingDelayedTasksAfterShutdown); + } + + /** + * Main execution method for delayed or periodic tasks. If pool + * is shut down, rejects the task. Otherwise adds task to queue + * and starts a thread, if necessary, to run it. (We cannot + * prestart the thread to run the task because the task (probably) + * shouldn't be run yet,) If the pool is shut down while the task + * is being added, cancel and remove it if required by state and + * run-after-shutdown parameters. + * + * @param task the task + */ + private void delayedExecute(ScheduledFutureTask<?> task) { + if (isShutdown()) + reject(task); + else { + super.getQueue().add(task); + if (isShutdown() && + !canRunInCurrentRunState(task.isPeriodic()) && + remove(task)) + task.cancel(false); + else + prestartCoreThread(); } } /** - * Specialized variant of ThreadPoolExecutor.execute for delayed tasks. - */ - private void delayedExecute(Runnable command) { - if (isShutdown()) { - reject(command); - return; - } - // Prestart a thread if necessary. We cannot prestart it - // running the task because the task (probably) shouldn't be - // run yet, so thread will just idle until delay elapses. - if (getPoolSize() < getCorePoolSize()) - prestartCoreThread(); - - super.getQueue().add(command); + * Requeues a periodic task unless current run state precludes it. + * Same idea as delayedExecute except drops task rather than rejecting. + * + * @param task the task + */ + void reExecutePeriodic(ScheduledFutureTask<?> task) { + if (canRunInCurrentRunState(true)) { + super.getQueue().add(task); + if (!canRunInCurrentRunState(true) && remove(task)) + task.cancel(false); + else + prestartCoreThread(); + } } /** - * Cancel and clear the queue of all tasks that should not be run - * due to shutdown policy. - */ - private void cancelUnwantedTasks() { - boolean keepDelayed = getExecuteExistingDelayedTasksAfterShutdownPolicy(); - boolean keepPeriodic = getContinueExistingPeriodicTasksAfterShutdownPolicy(); - if (!keepDelayed && !keepPeriodic) - super.getQueue().clear(); - else if (keepDelayed || keepPeriodic) { - Object[] entries = super.getQueue().toArray(); - for (int i = 0; i < entries.length; ++i) { - Object e = entries[i]; + * Cancels and clears the queue of all tasks that should not be run + * due to shutdown policy. Invoked within super.shutdown. + */ + @Override void onShutdown() { + BlockingQueue<Runnable> q = super.getQueue(); + boolean keepDelayed = + getExecuteExistingDelayedTasksAfterShutdownPolicy(); + boolean keepPeriodic = + getContinueExistingPeriodicTasksAfterShutdownPolicy(); + if (!keepDelayed && !keepPeriodic) + q.clear(); + else { + // Traverse snapshot to avoid iterator exceptions + for (Object e : q.toArray()) { if (e instanceof ScheduledFutureTask) { - ScheduledFutureTask<?> t = (ScheduledFutureTask<?>)e; - if (t.isPeriodic()? !keepPeriodic : !keepDelayed) - t.cancel(false); + ScheduledFutureTask<?> t = + (ScheduledFutureTask<?>)e; + if ((t.isPeriodic() ? !keepPeriodic : !keepDelayed) || + t.isCancelled()) { // also remove if already cancelled + if (q.remove(t)) + t.cancel(false); + } } } - entries = null; - purge(); } - } - - public boolean remove(Runnable task) { - if (!(task instanceof ScheduledFutureTask)) - return false; - return getQueue().remove(task); + tryTerminate(); } /** - * Creates a new ScheduledThreadPoolExecutor with the given core - * pool size. - * - * @param corePoolSize the number of threads to keep in the pool, - * even if they are idle. - * @throws IllegalArgumentException if corePoolSize less than or - * equal to zero + * Creates a new {@code ScheduledThreadPoolExecutor} with the + * given core pool size. + * + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle + * @throws IllegalArgumentException if {@code corePoolSize < 0} */ public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS, @@ -230,14 +330,15 @@ public class ScheduledThreadPoolExecutor } /** - * Creates a new ScheduledThreadPoolExecutor with the given - * initial parameters. - * - * @param corePoolSize the number of threads to keep in the pool, - * even if they are idle. + * Creates a new {@code ScheduledThreadPoolExecutor} with the + * given initial parameters. + * + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle * @param threadFactory the factory to use when the executor - * creates a new thread. - * @throws NullPointerException if threadFactory is null + * creates a new thread + * @throws IllegalArgumentException if {@code corePoolSize < 0} + * @throws NullPointerException if {@code threadFactory} is null */ public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) { @@ -248,12 +349,13 @@ public class ScheduledThreadPoolExecutor /** * Creates a new ScheduledThreadPoolExecutor with the given * initial parameters. - * - * @param corePoolSize the number of threads to keep in the pool, - * even if they are idle. + * + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle * @param handler the handler to use when execution is blocked - * because the thread bounds and queue capacities are reached. - * @throws NullPointerException if handler is null + * because the thread bounds and queue capacities are reached + * @throws IllegalArgumentException if {@code corePoolSize < 0} + * @throws NullPointerException if {@code handler} is null */ public ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler) { @@ -264,14 +366,16 @@ public class ScheduledThreadPoolExecutor /** * Creates a new ScheduledThreadPoolExecutor with the given * initial parameters. - * - * @param corePoolSize the number of threads to keep in the pool, - * even if they are idle. + * + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle * @param threadFactory the factory to use when the executor - * creates a new thread. + * creates a new thread * @param handler the handler to use when execution is blocked - * because the thread bounds and queue capacities are reached. - * @throws NullPointerException if threadFactory or handler is null + * because the thread bounds and queue capacities are reached + * @throws IllegalArgumentException if {@code corePoolSize < 0} + * @throws NullPointerException if {@code threadFactory} or + * {@code handler} is null */ public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, @@ -280,128 +384,178 @@ public class ScheduledThreadPoolExecutor new DelayedWorkQueue(), threadFactory, handler); } - public ScheduledFuture<?> schedule(Runnable command, - long delay, + /** + * Returns the trigger time of a delayed action + */ + private static long nextTriggerTime(long delay, TimeUnit unit) { + long triggerTime; + long now = now(); + if (delay <= 0) + return now; // avoid negative trigger times + else if ((triggerTime = now + unit.toNanos(delay)) < 0) + return Long.MAX_VALUE; // avoid numerical overflow + else + return triggerTime; + } + + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ + public ScheduledFuture<?> schedule(Runnable command, + long delay, TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); - long triggerTime = now() + unit.toNanos(delay); - ScheduledFutureTask<?> t = - new ScheduledFutureTask<Boolean>(command, null, triggerTime); + long triggerTime = nextTriggerTime(delay, unit); + ScheduledFutureTask<?> t + = new ScheduledFutureTask<Void>(command, null, triggerTime); delayedExecute(t); return t; } - public <V> ScheduledFuture<V> schedule(Callable<V> callable, - long delay, + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ + public <V> ScheduledFuture<V> schedule(Callable<V> callable, + long delay, TimeUnit unit) { if (callable == null || unit == null) throw new NullPointerException(); - if (delay < 0) delay = 0; - long triggerTime = now() + unit.toNanos(delay); - ScheduledFutureTask<V> t = - new ScheduledFutureTask<V>(callable, triggerTime); + long triggerTime = nextTriggerTime(delay, unit); + ScheduledFutureTask<V> t + = new ScheduledFutureTask<V>(callable, triggerTime); delayedExecute(t); return t; } - public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, - long initialDelay, - long period, + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, + long initialDelay, + long period, TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); if (period <= 0) throw new IllegalArgumentException(); if (initialDelay < 0) initialDelay = 0; - long triggerTime = now() + unit.toNanos(initialDelay); - ScheduledFutureTask<?> t = - new ScheduledFutureTask<Object>(command, - null, - triggerTime, - unit.toNanos(period)); - delayedExecute(t); - return t; + long triggerTime = nextTriggerTime(initialDelay, unit); + ScheduledFutureTask<Void> sft = + new ScheduledFutureTask<Void>(command, + null, + triggerTime, + unit.toNanos(period)); + sft.outerTask = sft; + delayedExecute(sft); + return sft; } - - public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, - long initialDelay, - long delay, + + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, + long initialDelay, + long delay, TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); if (delay <= 0) throw new IllegalArgumentException(); - if (initialDelay < 0) initialDelay = 0; - long triggerTime = now() + unit.toNanos(initialDelay); - ScheduledFutureTask<?> t = - new ScheduledFutureTask<Boolean>(command, - null, - triggerTime, - unit.toNanos(-delay)); - delayedExecute(t); - return t; + long triggerTime = nextTriggerTime(initialDelay, unit); + ScheduledFutureTask<Void> sft = + new ScheduledFutureTask<Void>(command, + null, + triggerTime, + unit.toNanos(-delay)); + sft.outerTask = sft; + delayedExecute(sft); + return sft; } - /** - * Execute command with zero required delay. This has effect - * equivalent to <tt>schedule(command, 0, anyUnit)</tt>. Note - * that inspections of the queue and of the list returned by - * <tt>shutdownNow</tt> will access the zero-delayed - * {@link ScheduledFuture}, not the <tt>command</tt> itself. + * Executes {@code command} with zero required delay. + * This has effect equivalent to + * {@link #schedule(Runnable,long,TimeUnit) schedule(command, 0, anyUnit)}. + * Note that inspections of the queue and of the list returned by + * {@code shutdownNow} will access the zero-delayed + * {@link ScheduledFuture}, not the {@code command} itself. + * + * <p>A consequence of the use of {@code ScheduledFuture} objects is + * that {@link ThreadPoolExecutor#afterExecute afterExecute} is always + * called with a null second {@code Throwable} argument, even if the + * {@code command} terminated abruptly. Instead, the {@code Throwable} + * thrown by such a task can be obtained via {@link Future#get}. * - * @param command the task to execute * @throws RejectedExecutionException at discretion of - * <tt>RejectedExecutionHandler</tt>, if task cannot be accepted - * for execution because the executor has been shut down. - * @throws NullPointerException if command is null + * {@code RejectedExecutionHandler}, if the task + * cannot be accepted for execution because the + * executor has been shut down + * @throws NullPointerException {@inheritDoc} */ public void execute(Runnable command) { - if (command == null) - throw new NullPointerException(); schedule(command, 0, TimeUnit.NANOSECONDS); } // Override AbstractExecutorService methods + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ public Future<?> submit(Runnable task) { return schedule(task, 0, TimeUnit.NANOSECONDS); } + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ public <T> Future<T> submit(Runnable task, T result) { - return schedule(Executors.callable(task, result), + return schedule(Executors.callable(task, result), 0, TimeUnit.NANOSECONDS); } + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ public <T> Future<T> submit(Callable<T> task) { return schedule(task, 0, TimeUnit.NANOSECONDS); } /** - * Set policy on whether to continue executing existing periodic - * tasks even when this executor has been <tt>shutdown</tt>. In - * this case, these tasks will only terminate upon - * <tt>shutdownNow</tt>, or after setting the policy to - * <tt>false</tt> when already shutdown. This value is by default - * false. - * @param value if true, continue after shutdown, else don't. - * @see #getExecuteExistingDelayedTasksAfterShutdownPolicy + * Sets the policy on whether to continue executing existing + * periodic tasks even when this executor has been {@code shutdown}. + * In this case, these tasks will only terminate upon + * {@code shutdownNow} or after setting the policy to + * {@code false} when already shutdown. + * This value is by default {@code false}. + * + * @param value if {@code true}, continue after shutdown, else don't. + * @see #getContinueExistingPeriodicTasksAfterShutdownPolicy */ public void setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean value) { continueExistingPeriodicTasksAfterShutdown = value; if (!value && isShutdown()) - cancelUnwantedTasks(); + onShutdown(); } /** - * Get the policy on whether to continue executing existing - * periodic tasks even when this executor has been - * <tt>shutdown</tt>. In this case, these tasks will only - * terminate upon <tt>shutdownNow</tt> or after setting the policy - * to <tt>false</tt> when already shutdown. This value is by - * default false. - * @return true if will continue after shutdown. + * Gets the policy on whether to continue executing existing + * periodic tasks even when this executor has been {@code shutdown}. + * In this case, these tasks will only terminate upon + * {@code shutdownNow} or after setting the policy to + * {@code false} when already shutdown. + * This value is by default {@code false}. + * + * @return {@code true} if will continue after shutdown * @see #setContinueExistingPeriodicTasksAfterShutdownPolicy */ public boolean getContinueExistingPeriodicTasksAfterShutdownPolicy() { @@ -409,66 +563,79 @@ public class ScheduledThreadPoolExecutor } /** - * Set policy on whether to execute existing delayed - * tasks even when this executor has been <tt>shutdown</tt>. In - * this case, these tasks will only terminate upon - * <tt>shutdownNow</tt>, or after setting the policy to - * <tt>false</tt> when already shutdown. This value is by default - * true. - * @param value if true, execute after shutdown, else don't. + * Sets the policy on whether to execute existing delayed + * tasks even when this executor has been {@code shutdown}. + * In this case, these tasks will only terminate upon + * {@code shutdownNow}, or after setting the policy to + * {@code false} when already shutdown. + * This value is by default {@code true}. + * + * @param value if {@code true}, execute after shutdown, else don't. * @see #getExecuteExistingDelayedTasksAfterShutdownPolicy */ public void setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean value) { executeExistingDelayedTasksAfterShutdown = value; if (!value && isShutdown()) - cancelUnwantedTasks(); + onShutdown(); } /** - * Get policy on whether to execute existing delayed - * tasks even when this executor has been <tt>shutdown</tt>. In - * this case, these tasks will only terminate upon - * <tt>shutdownNow</tt>, or after setting the policy to - * <tt>false</tt> when already shutdown. This value is by default - * true. - * @return true if will execute after shutdown. + * Gets the policy on whether to execute existing delayed + * tasks even when this executor has been {@code shutdown}. + * In this case, these tasks will only terminate upon + * {@code shutdownNow}, or after setting the policy to + * {@code false} when already shutdown. + * This value is by default {@code true}. + * + * @return {@code true} if will execute after shutdown * @see #setExecuteExistingDelayedTasksAfterShutdownPolicy */ public boolean getExecuteExistingDelayedTasksAfterShutdownPolicy() { return executeExistingDelayedTasksAfterShutdown; } - /** * Initiates an orderly shutdown in which previously submitted - * tasks are executed, but no new tasks will be accepted. If the - * <tt>ExecuteExistingDelayedTasksAfterShutdownPolicy</tt> has - * been set <tt>false</tt>, existing delayed tasks whose delays - * have not yet elapsed are cancelled. And unless the - * <tt>ContinueExistingPeriodicTasksAfterShutdownPolicy</tt> has - * been set <tt>true</tt>, future executions of existing periodic - * tasks will be cancelled. + * tasks are executed, but no new tasks will be accepted. + * Invocation has no additional effect if already shut down. + * + * <p>This method does not wait for previously submitted tasks to + * complete execution. Use {@link #awaitTermination awaitTermination} + * to do that. + * + * <p>If the {@code ExecuteExistingDelayedTasksAfterShutdownPolicy} + * has been set {@code false}, existing delayed tasks whose delays + * have not yet elapsed are cancelled. And unless the {@code + * ContinueExistingPeriodicTasksAfterShutdownPolicy} has been set + * {@code true}, future executions of existing periodic tasks will + * be cancelled. + * + * @throws SecurityException {@inheritDoc} */ public void shutdown() { - cancelUnwantedTasks(); super.shutdown(); } /** * Attempts to stop all actively executing tasks, halts the - * processing of waiting tasks, and returns a list of the tasks that were - * awaiting execution. - * + * processing of waiting tasks, and returns a list of the tasks + * that were awaiting execution. + * + * <p>This method does not wait for actively executing tasks to + * terminate. Use {@link #awaitTermination awaitTermination} to + * do that. + * * <p>There are no guarantees beyond best-effort attempts to stop * processing actively executing tasks. This implementation - * cancels tasks via {@link Thread#interrupt}, so if any tasks mask or - * fail to respond to interrupts, they may never terminate. + * cancels tasks via {@link Thread#interrupt}, so any task that + * fails to respond to interrupts may never terminate. * - * @return list of tasks that never commenced execution. Each - * element of this list is a {@link ScheduledFuture}, - * including those tasks submitted using <tt>execute</tt>, which - * are for scheduling purposes used as the basis of a zero-delay - * <tt>ScheduledFuture</tt>. + * @return list of tasks that never commenced execution. + * Each element of this list is a {@link ScheduledFuture}, + * including those tasks submitted using {@code execute}, + * which are for scheduling purposes used as the basis of a + * zero-delay {@code ScheduledFuture}. + * @throws SecurityException {@inheritDoc} */ public List<Runnable> shutdownNow() { return super.shutdownNow(); @@ -477,9 +644,9 @@ public class ScheduledThreadPoolExecutor /** * Returns the task queue used by this executor. Each element of * this queue is a {@link ScheduledFuture}, including those - * tasks submitted using <tt>execute</tt> which are for scheduling + * tasks submitted using {@code execute} which are for scheduling * purposes used as the basis of a zero-delay - * <tt>ScheduledFuture</tt>. Iteration over this queue is + * {@code ScheduledFuture}. Iteration over this queue is * <em>not</em> guaranteed to traverse tasks in the order in * which they will execute. * @@ -490,52 +657,478 @@ public class ScheduledThreadPoolExecutor } /** - * An annoying wrapper class to convince generics compiler to - * use a DelayQueue<ScheduledFutureTask> as a BlockingQueue<Runnable> - */ - private static class DelayedWorkQueue - extends AbstractCollection<Runnable> + * Specialized delay queue. To mesh with TPE declarations, this + * class must be declared as a BlockingQueue<Runnable> even though + * it can only hold RunnableScheduledFutures. + */ + static class DelayedWorkQueue extends AbstractQueue<Runnable> implements BlockingQueue<Runnable> { - - private final DelayQueue<ScheduledFutureTask> dq = new DelayQueue<ScheduledFutureTask>(); - public Runnable poll() { return dq.poll(); } - public Runnable peek() { return dq.peek(); } - public Runnable take() throws InterruptedException { return dq.take(); } - public Runnable poll(long timeout, TimeUnit unit) throws InterruptedException { - return dq.poll(timeout, unit); - } - - public boolean add(Runnable x) { return dq.add((ScheduledFutureTask)x); } - public boolean offer(Runnable x) { return dq.offer((ScheduledFutureTask)x); } - public void put(Runnable x) { - dq.put((ScheduledFutureTask)x); - } - public boolean offer(Runnable x, long timeout, TimeUnit unit) { - return dq.offer((ScheduledFutureTask)x, timeout, unit); - } - - public Runnable remove() { return dq.remove(); } - public Runnable element() { return dq.element(); } - public void clear() { dq.clear(); } - public int drainTo(Collection<? super Runnable> c) { return dq.drainTo(c); } - public int drainTo(Collection<? super Runnable> c, int maxElements) { - return dq.drainTo(c, maxElements); - } - - public int remainingCapacity() { return dq.remainingCapacity(); } - public boolean remove(Object x) { return dq.remove(x); } - public boolean contains(Object x) { return dq.contains(x); } - public int size() { return dq.size(); } - public boolean isEmpty() { return dq.isEmpty(); } - public Object[] toArray() { return dq.toArray(); } - public <T> T[] toArray(T[] array) { return dq.toArray(array); } - public Iterator<Runnable> iterator() { - return new Iterator<Runnable>() { - private Iterator<ScheduledFutureTask> it = dq.iterator(); - public boolean hasNext() { return it.hasNext(); } - public Runnable next() { return it.next(); } - public void remove() { it.remove(); } - }; + + /* + * A DelayedWorkQueue is based on a heap-based data structure + * like those in DelayQueue and PriorityQueue, except that + * every ScheduledFutureTask also records its index into the + * heap array. This eliminates the need to find a task upon + * cancellation, greatly speeding up removal (down from O(n) + * to O(log n)), and reducing garbage retention that would + * otherwise occur by waiting for the element to rise to top + * before clearing. But because the queue may also hold + * RunnableScheduledFutures that are not ScheduledFutureTasks, + * we are not guaranteed to have such indices available, in + * which case we fall back to linear search. (We expect that + * most tasks will not be decorated, and that the faster cases + * will be much more common.) + * + * All heap operations must record index changes -- mainly + * within siftUp and siftDown. Upon removal, a task's + * heapIndex is set to -1. Note that ScheduledFutureTasks can + * appear at most once in the queue (this need not be true for + * other kinds of tasks or work queues), so are uniquely + * identified by heapIndex. + */ + + private static final int INITIAL_CAPACITY = 16; + private ScheduledFutureTask[] queue = + new ScheduledFutureTask[INITIAL_CAPACITY]; + private final ReentrantLock lock = new ReentrantLock(); + private int size = 0; + + /** + * Thread designated to wait for the task at the head of the + * queue. This variant of the Leader-Follower pattern + * (http://www.cs.wustl.edu/~schmidt/POSA/POSA2/) serves to + * minimize unnecessary timed waiting. When a thread becomes + * the leader, it waits only for the next delay to elapse, but + * other threads await indefinitely. The leader thread must + * signal some other thread before returning from take() or + * poll(...), unless some other thread becomes leader in the + * interim. Whenever the head of the queue is replaced with a + * task with an earlier expiration time, the leader field is + * invalidated by being reset to null, and some waiting + * thread, but not necessarily the current leader, is + * signalled. So waiting threads must be prepared to acquire + * and lose leadership while waiting. + */ + private Thread leader = null; + + /** + * Condition signalled when a newer task becomes available at the + * head of the queue or a new thread may need to become leader. + */ + private final Condition available = lock.newCondition(); + + /** + * Set f's heapIndex if it is a ScheduledFutureTask. + */ + private void setIndex(ScheduledFutureTask f, int idx) { + if (f instanceof ScheduledFutureTask) + ((ScheduledFutureTask)f).heapIndex = idx; + } + + /** + * Sift element added at bottom up to its heap-ordered spot. + * Call only when holding lock. + */ + private void siftUp(int k, ScheduledFutureTask key) { + while (k > 0) { + int parent = (k - 1) >>> 1; + ScheduledFutureTask e = queue[parent]; + if (key.compareTo(e) >= 0) + break; + queue[k] = e; + setIndex(e, k); + k = parent; + } + queue[k] = key; + setIndex(key, k); + } + + /** + * Sift element added at top down to its heap-ordered spot. + * Call only when holding lock. + */ + private void siftDown(int k, ScheduledFutureTask key) { + int half = size >>> 1; + while (k < half) { + int child = (k << 1) + 1; + ScheduledFutureTask c = queue[child]; + int right = child + 1; + if (right < size && c.compareTo(queue[right]) > 0) + c = queue[child = right]; + if (key.compareTo(c) <= 0) + break; + queue[k] = c; + setIndex(c, k); + k = child; + } + queue[k] = key; + setIndex(key, k); + } + + /** + * Resize the heap array. Call only when holding lock. + */ + private void grow() { + int oldCapacity = queue.length; + int newCapacity = oldCapacity + (oldCapacity >> 1); // grow 50% + if (newCapacity < 0) // overflow + newCapacity = Integer.MAX_VALUE; + queue = Java6Arrays.copyOf(queue, newCapacity); + } + + /** + * Find index of given object, or -1 if absent + */ + private int indexOf(Object x) { + if (x != null) { + if (x instanceof ScheduledFutureTask) { + int i = ((ScheduledFutureTask) x).heapIndex; + // Sanity check; x could conceivably be a + // ScheduledFutureTask from some other pool. + if (i >= 0 && i < size && queue[i] == x) + return i; + } else { + for (int i = 0; i < size; i++) + if (x.equals(queue[i])) + return i; + } + } + return -1; + } + + public boolean contains(Object x) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return indexOf(x) != -1; + } finally { + lock.unlock(); + } + } + + public boolean remove(Object x) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + int i = indexOf(x); + if (i < 0) + return false; + + setIndex(queue[i], -1); + int s = --size; + ScheduledFutureTask replacement = queue[s]; + queue[s] = null; + if (s != i) { + siftDown(i, replacement); + if (queue[i] == replacement) + siftUp(i, replacement); + } + return true; + } finally { + lock.unlock(); + } + } + + public int size() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return size; + } finally { + lock.unlock(); + } + } + + public boolean isEmpty() { + return size() == 0; + } + + public int remainingCapacity() { + return Integer.MAX_VALUE; + } + + public ScheduledFutureTask peek() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return queue[0]; + } finally { + lock.unlock(); + } + } + + public boolean offer(Runnable x) { + if (x == null) + throw new NullPointerException(); + ScheduledFutureTask e = (ScheduledFutureTask)x; + final ReentrantLock lock = this.lock; + lock.lock(); + try { + int i = size; + if (i >= queue.length) + grow(); + size = i + 1; + if (i == 0) { + queue[0] = e; + setIndex(e, 0); + } else { + siftUp(i, e); + } + if (queue[0] == e) { + leader = null; + available.signal(); + } + } finally { + lock.unlock(); + } + return true; + } + + public void put(Runnable e) { + offer(e); + } + + public boolean add(Runnable e) { + return offer(e); + } + + public boolean offer(Runnable e, long timeout, TimeUnit unit) { + return offer(e); + } + + /** + * Performs common bookkeeping for poll and take: Replaces + * first element with last and sifts it down. Call only when + * holding lock. + * @param f the task to remove and return + */ + private ScheduledFutureTask finishPoll(ScheduledFutureTask f) { + int s = --size; + ScheduledFutureTask x = queue[s]; + queue[s] = null; + if (s != 0) + siftDown(0, x); + setIndex(f, -1); + return f; + } + + public ScheduledFutureTask poll() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + ScheduledFutureTask first = queue[0]; + if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0) + return null; + else + return finishPoll(first); + } finally { + lock.unlock(); + } + } + + public ScheduledFutureTask take() throws InterruptedException { + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + try { + for (;;) { + ScheduledFutureTask first = queue[0]; + if (first == null) + available.await(); + else { + long delay = first.getDelay(TimeUnit.NANOSECONDS); + if (delay <= 0) + return finishPoll(first); + else if (leader != null) + available.await(); + else { + Thread thisThread = Thread.currentThread(); + leader = thisThread; + try { + available.awaitNanos(delay); + } finally { + if (leader == thisThread) + leader = null; + } + } + } + } + } finally { + if (leader == null && queue[0] != null) + available.signal(); + lock.unlock(); + } + } + + public ScheduledFutureTask poll(long timeout, TimeUnit unit) + throws InterruptedException { + long nanos = unit.toNanos(timeout); + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + try { + for (;;) { + ScheduledFutureTask first = queue[0]; + if (first == null) { + if (nanos <= 0) + return null; + else + nanos = available.awaitNanos(nanos); + } else { + long delay = first.getDelay(TimeUnit.NANOSECONDS); + if (delay <= 0) + return finishPoll(first); + if (nanos <= 0) + return null; + if (nanos < delay || leader != null) + nanos = available.awaitNanos(nanos); + else { + Thread thisThread = Thread.currentThread(); + leader = thisThread; + try { + long timeLeft = available.awaitNanos(delay); + nanos -= delay - timeLeft; + } finally { + if (leader == thisThread) + leader = null; + } + } + } + } + } finally { + if (leader == null && queue[0] != null) + available.signal(); + lock.unlock(); + } + } + + public void clear() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + for (int i = 0; i < size; i++) { + ScheduledFutureTask t = queue[i]; + if (t != null) { + queue[i] = null; + setIndex(t, -1); + } + } + size = 0; + } finally { + lock.unlock(); + } + } + + /** + * Return and remove first element only if it is expired. + * Used only by drainTo. Call only when holding lock. + */ + private ScheduledFutureTask pollExpired() { + ScheduledFutureTask first = queue[0]; + if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0) + return null; + return finishPoll(first); + } + + public int drainTo(Collection<? super Runnable> c) { + if (c == null) + throw new NullPointerException(); + if (c == this) + throw new IllegalArgumentException(); + final ReentrantLock lock = this.lock; + lock.lock(); + try { + ScheduledFutureTask first; + int n = 0; + while ((first = pollExpired()) != null) { + c.add(first); + ++n; + } + return n; + } finally { + lock.unlock(); + } + } + + public int drainTo(Collection<? super Runnable> c, int maxElements) { + if (c == null) + throw new NullPointerException(); + if (c == this) + throw new IllegalArgumentException(); + if (maxElements <= 0) + return 0; + final ReentrantLock lock = this.lock; + lock.lock(); + try { + ScheduledFutureTask first; + int n = 0; + while (n < maxElements && (first = pollExpired()) != null) { + c.add(first); + ++n; + } + return n; + } finally { + lock.unlock(); + } + } + + public Object[] toArray() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return Java6Arrays.copyOf(queue, size, Object[].class); + } finally { + lock.unlock(); + } + } + + @SuppressWarnings("unchecked") + public <T> T[] toArray(T[] a) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + if (a.length < size) + return (T[]) Java6Arrays.copyOf(queue, size, a.getClass()); + System.arraycopy(queue, 0, a, 0, size); + if (a.length > size) + a[size] = null; + return a; + } finally { + lock.unlock(); + } + } + + public Iterator<Runnable> iterator() { + return new Itr(Java6Arrays.copyOf(queue, size)); + } + + /** + * Snapshot iterator that works off copy of underlying q array. + */ + private class Itr implements Iterator<Runnable> { + final ScheduledFutureTask[] array; + int cursor = 0; // index of next element to return + int lastRet = -1; // index of last element, or -1 if no such + + Itr(ScheduledFutureTask[] array) { + this.array = array; + } + + public boolean hasNext() { + return cursor < array.length; + } + + public Runnable next() { + if (cursor >= array.length) + throw new NoSuchElementException(); + lastRet = cursor; + return array[cursor++]; + } + + public void remove() { + if (lastRet < 0) + throw new IllegalStateException(); + DelayedWorkQueue.this.remove(array[lastRet]); + lastRet = -1; + } } } } diff --git a/concurrent/src/main/java/java/util/concurrent/Semaphore.java b/concurrent/src/main/java/java/util/concurrent/Semaphore.java index 6eb80bc..1052364 100644 --- a/concurrent/src/main/java/java/util/concurrent/Semaphore.java +++ b/concurrent/src/main/java/java/util/concurrent/Semaphore.java @@ -14,7 +14,7 @@ import java.util.concurrent.atomic.*; * permits. Each {@link #acquire} blocks if necessary until a permit is * available, and then takes it. Each {@link #release} adds a permit, * potentially releasing a blocking acquirer. - * However, no actual permit objects are used; the <tt>Semaphore</tt> just + * However, no actual permit objects are used; the {@code Semaphore} just * keeps a count of the number available and acts accordingly. * * <p>Semaphores are often used to restrict the number of threads than can @@ -22,7 +22,7 @@ import java.util.concurrent.atomic.*; * a class that uses a semaphore to control access to a pool of items: * <pre> * class Pool { - * private static final MAX_AVAILABLE = 100; + * private static final int MAX_AVAILABLE = 100; * private final Semaphore available = new Semaphore(MAX_AVAILABLE, true); * * public Object getItem() throws InterruptedException { @@ -93,15 +93,19 @@ import java.util.concurrent.atomic.*; * guarantees about the order in which threads acquire permits. In * particular, <em>barging</em> is permitted, that is, a thread * invoking {@link #acquire} can be allocated a permit ahead of a - * thread that has been waiting. When fairness is set true, the + * thread that has been waiting - logically the new thread places itself at + * the head of the queue of waiting threads. When fairness is set true, the * semaphore guarantees that threads invoking any of the {@link - * #acquire() acquire} methods are allocated permits in the order in + * #acquire() acquire} methods are selected to obtain permits in the order in * which their invocation of those methods was processed * (first-in-first-out; FIFO). Note that FIFO ordering necessarily * applies to specific internal points of execution within these * methods. So, it is possible for one thread to invoke - * <tt>acquire</tt> before another, but reach the ordering point after + * {@code acquire} before another, but reach the ordering point after * the other, and similarly upon return from the method. + * Also note that the untimed {@link #tryAcquire() tryAcquire} methods do not + * honor the fairness setting, but will take any permits that are + * available. * * <p>Generally, semaphores used to control resource access should be * initialized as fair, to ensure that no thread is starved out from @@ -114,6 +118,12 @@ import java.util.concurrent.atomic.*; * permits at a time. Beware of the increased risk of indefinite * postponement when these methods are used without fairness set true. * + * <p>Memory consistency effects: Actions in a thread prior to calling + * a "release" method such as {@code release()} + * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> + * actions following a successful "acquire" method such as {@code acquire()} + * in another thread. + * * @since 1.5 * @author Doug Lea * @@ -130,10 +140,12 @@ public class Semaphore implements java.io.Serializable { * versions. */ abstract static class Sync extends AbstractQueuedSynchronizer { + private static final long serialVersionUID = 1192457210091910933L; + Sync(int permits) { setState(permits); } - + final int getPermits() { return getState(); } @@ -147,11 +159,11 @@ public class Semaphore implements java.io.Serializable { return remaining; } } - + protected final boolean tryReleaseShared(int releases) { for (;;) { int p = getState(); - if (compareAndSetState(p, p + releases)) + if (compareAndSetState(p, p + releases)) return true; } } @@ -178,10 +190,12 @@ public class Semaphore implements java.io.Serializable { * NonFair version */ final static class NonfairSync extends Sync { + private static final long serialVersionUID = -2694183684443567898L; + NonfairSync(int permits) { super(permits); } - + protected int tryAcquireShared(int acquires) { return nonfairTryAcquireShared(acquires); } @@ -191,10 +205,12 @@ public class Semaphore implements java.io.Serializable { * Fair version */ final static class FairSync extends Sync { + private static final long serialVersionUID = 2014338818796000944L; + FairSync(int permits) { super(permits); } - + protected int tryAcquireShared(int acquires) { Thread current = Thread.currentThread(); for (;;) { @@ -211,57 +227,59 @@ public class Semaphore implements java.io.Serializable { } /** - * Creates a <tt>Semaphore</tt> with the given number of + * Creates a {@code Semaphore} with the given number of * permits and nonfair fairness setting. - * @param permits the initial number of permits available. This - * value may be negative, in which case releases must - * occur before any acquires will be granted. + * + * @param permits the initial number of permits available. + * This value may be negative, in which case releases + * must occur before any acquires will be granted. */ - public Semaphore(int permits) { + public Semaphore(int permits) { sync = new NonfairSync(permits); } /** - * Creates a <tt>Semaphore</tt> with the given number of + * Creates a {@code Semaphore} with the given number of * permits and the given fairness setting. - * @param permits the initial number of permits available. This - * value may be negative, in which case releases must - * occur before any acquires will be granted. - * @param fair true if this semaphore will guarantee first-in - * first-out granting of permits under contention, else false. + * + * @param permits the initial number of permits available. + * This value may be negative, in which case releases + * must occur before any acquires will be granted. + * @param fair {@code true} if this semaphore will guarantee + * first-in first-out granting of permits under contention, + * else {@code false} */ - public Semaphore(int permits, boolean fair) { + public Semaphore(int permits, boolean fair) { sync = (fair)? new FairSync(permits) : new NonfairSync(permits); } /** * Acquires a permit from this semaphore, blocking until one is - * available, or the thread is {@link Thread#interrupt interrupted}. + * available, or the thread is {@linkplain Thread#interrupt interrupted}. * * <p>Acquires a permit, if one is available and returns immediately, * reducing the number of available permits by one. + * * <p>If no permit is available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of two things happens: * <ul> * <li>Some other thread invokes the {@link #release} method for this * semaphore and the current thread is next to be assigned a permit; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread. + * <li>Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread. * </ul> * * <p>If the current thread: * <ul> * <li>has its interrupted status set on entry to this method; or - * <li>is {@link Thread#interrupt interrupted} while waiting + * <li>is {@linkplain Thread#interrupt interrupted} while waiting * for a permit, * </ul> * then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * * @throws InterruptedException if the current thread is interrupted - * - * @see Thread#interrupt */ public void acquire() throws InterruptedException { sync.acquireSharedInterruptibly(1); @@ -273,103 +291,105 @@ public class Semaphore implements java.io.Serializable { * * <p>Acquires a permit, if one is available and returns immediately, * reducing the number of available permits by one. + * * <p>If no permit is available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * some other thread invokes the {@link #release} method for this * semaphore and the current thread is next to be assigned a permit. * - * <p>If the current thread - * is {@link Thread#interrupt interrupted} while waiting - * for a permit then it will continue to wait, but the time at which - * the thread is assigned a permit may change compared to the time it - * would have received the permit had no interruption occurred. When the - * thread does return from this method its interrupt status will be set. - * + * <p>If the current thread is {@linkplain Thread#interrupt interrupted} + * while waiting for a permit then it will continue to wait, but the + * time at which the thread is assigned a permit may change compared to + * the time it would have received the permit had no interruption + * occurred. When the thread does return from this method its interrupt + * status will be set. */ public void acquireUninterruptibly() { sync.acquireShared(1); } /** - * Acquires a permit from this semaphore, only if one is available at the + * Acquires a permit from this semaphore, only if one is available at the * time of invocation. + * * <p>Acquires a permit, if one is available and returns immediately, - * with the value <tt>true</tt>, + * with the value {@code true}, * reducing the number of available permits by one. * * <p>If no permit is available then this method will return - * immediately with the value <tt>false</tt>. + * immediately with the value {@code false}. * * <p>Even when this semaphore has been set to use a - * fair ordering policy, a call to <tt>tryAcquire()</tt> <em>will</em> + * fair ordering policy, a call to {@code tryAcquire()} <em>will</em> * immediately acquire a permit if one is available, whether or not - * other threads are currently waiting. - * This "barging" behavior can be useful in certain + * other threads are currently waiting. + * This "barging" behavior can be useful in certain * circumstances, even though it breaks fairness. If you want to honor - * the fairness setting, then use + * the fairness setting, then use * {@link #tryAcquire(long, TimeUnit) tryAcquire(0, TimeUnit.SECONDS) } * which is almost equivalent (it also detects interruption). * - * @return <tt>true</tt> if a permit was acquired and <tt>false</tt> - * otherwise. + * @return {@code true} if a permit was acquired and {@code false} + * otherwise */ public boolean tryAcquire() { return sync.nonfairTryAcquireShared(1) >= 0; } /** - * Acquires a permit from this semaphore, if one becomes available - * within the given waiting time and the - * current thread has not been {@link Thread#interrupt interrupted}. + * Acquires a permit from this semaphore, if one becomes available + * within the given waiting time and the current thread has not + * been {@linkplain Thread#interrupt interrupted}. + * * <p>Acquires a permit, if one is available and returns immediately, - * with the value <tt>true</tt>, + * with the value {@code true}, * reducing the number of available permits by one. - * <p>If no permit is available then - * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until one of three things happens: + * + * <p>If no permit is available then the current thread becomes + * disabled for thread scheduling purposes and lies dormant until + * one of three things happens: * <ul> * <li>Some other thread invokes the {@link #release} method for this * semaphore and the current thread is next to be assigned a permit; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + * <li>Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or * <li>The specified waiting time elapses. * </ul> - * <p>If a permit is acquired then the value <tt>true</tt> is returned. + * + * <p>If a permit is acquired then the value {@code true} is returned. + * * <p>If the current thread: * <ul> * <li>has its interrupted status set on entry to this method; or - * <li>is {@link Thread#interrupt interrupted} while waiting to acquire - * a permit, + * <li>is {@linkplain Thread#interrupt interrupted} while waiting + * to acquire a permit, * </ul> * then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. - * <p>If the specified waiting time elapses then the value <tt>false</tt> - * is returned. - * If the time is less than or equal to zero, the method will not wait - * at all. * - * @param timeout the maximum time to wait for a permit - * @param unit the time unit of the <tt>timeout</tt> argument. - * @return <tt>true</tt> if a permit was acquired and <tt>false</tt> - * if the waiting time elapsed before a permit was acquired. + * <p>If the specified waiting time elapses then the value {@code false} + * is returned. If the time is less than or equal to zero, the method + * will not wait at all. * + * @param timeout the maximum time to wait for a permit + * @param unit the time unit of the {@code timeout} argument + * @return {@code true} if a permit was acquired and {@code false} + * if the waiting time elapsed before a permit was acquired * @throws InterruptedException if the current thread is interrupted - * - * @see Thread#interrupt - * */ - public boolean tryAcquire(long timeout, TimeUnit unit) + public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); } /** * Releases a permit, returning it to the semaphore. - * <p>Releases a permit, increasing the number of available permits - * by one. - * If any threads are blocking trying to acquire a permit, then one - * is selected and given the permit that was just released. - * That thread is re-enabled for thread scheduling purposes. + * + * <p>Releases a permit, increasing the number of available permits by + * one. If any threads are trying to acquire a permit, then one is + * selected and given the permit that was just released. That thread + * is (re)enabled for thread scheduling purposes. + * * <p>There is no requirement that a thread that releases a permit must * have acquired that permit by calling {@link #acquire}. * Correct usage of a semaphore is established by programming convention @@ -378,45 +398,42 @@ public class Semaphore implements java.io.Serializable { public void release() { sync.releaseShared(1); } - + /** - * Acquires the given number of permits from this semaphore, - * blocking until all are available, - * or the thread is {@link Thread#interrupt interrupted}. + * Acquires the given number of permits from this semaphore, + * blocking until all are available, + * or the thread is {@linkplain Thread#interrupt interrupted}. * * <p>Acquires the given number of permits, if they are available, - * and returns immediately, - * reducing the number of available permits by the given amount. + * and returns immediately, reducing the number of available permits + * by the given amount. * * <p>If insufficient permits are available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of two things happens: * <ul> - * <li>Some other thread invokes one of the {@link #release() release} + * <li>Some other thread invokes one of the {@link #release() release} * methods for this semaphore, the current thread is next to be assigned * permits and the number of available permits satisfies this request; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread. + * <li>Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread. * </ul> * * <p>If the current thread: * <ul> * <li>has its interrupted status set on entry to this method; or - * <li>is {@link Thread#interrupt interrupted} while waiting + * <li>is {@linkplain Thread#interrupt interrupted} while waiting * for a permit, * </ul> * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. - * Any permits that were to be assigned to this thread are instead - * assigned to the next waiting thread(s), as if - * they had been made available by a call to {@link #release()}. + * interrupted status is cleared. + * Any permits that were to be assigned to this thread are instead + * assigned to other threads trying to acquire permits, as if + * permits had been made available by a call to {@link #release()}. * * @param permits the number of permits to acquire - * * @throws InterruptedException if the current thread is interrupted - * @throws IllegalArgumentException if permits less than zero. - * - * @see Thread#interrupt + * @throws IllegalArgumentException if {@code permits} is negative */ public void acquire(int permits) throws InterruptedException { if (permits < 0) throw new IllegalArgumentException(); @@ -424,27 +441,26 @@ public class Semaphore implements java.io.Serializable { } /** - * Acquires the given number of permits from this semaphore, + * Acquires the given number of permits from this semaphore, * blocking until all are available. * * <p>Acquires the given number of permits, if they are available, - * and returns immediately, - * reducing the number of available permits by the given amount. + * and returns immediately, reducing the number of available permits + * by the given amount. * * <p>If insufficient permits are available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until - * some other thread invokes one of the {@link #release() release} + * some other thread invokes one of the {@link #release() release} * methods for this semaphore, the current thread is next to be assigned * permits and the number of available permits satisfies this request. * - * <p>If the current thread - * is {@link Thread#interrupt interrupted} while waiting - * for permits then it will continue to wait and its position in the - * queue is not affected. When the - * thread does return from this method its interrupt status will be set. + * <p>If the current thread is {@linkplain Thread#interrupt interrupted} + * while waiting for permits then it will continue to wait and its + * position in the queue is not affected. When the thread does return + * from this method its interrupt status will be set. * * @param permits the number of permits to acquire - * @throws IllegalArgumentException if permits less than zero. + * @throws IllegalArgumentException if {@code permits} is negative * */ public void acquireUninterruptibly(int permits) { @@ -456,16 +472,16 @@ public class Semaphore implements java.io.Serializable { * Acquires the given number of permits from this semaphore, only * if all are available at the time of invocation. * - * <p>Acquires the given number of permits, if they are available, and - * returns immediately, with the value <tt>true</tt>, + * <p>Acquires the given number of permits, if they are available, and + * returns immediately, with the value {@code true}, * reducing the number of available permits by the given amount. * * <p>If insufficient permits are available then this method will return - * immediately with the value <tt>false</tt> and the number of available + * immediately with the value {@code false} and the number of available * permits is unchanged. * * <p>Even when this semaphore has been set to use a fair ordering - * policy, a call to <tt>tryAcquire</tt> <em>will</em> + * policy, a call to {@code tryAcquire} <em>will</em> * immediately acquire a permit if one is available, whether or * not other threads are currently waiting. This * "barging" behavior can be useful in certain @@ -475,10 +491,9 @@ public class Semaphore implements java.io.Serializable { * which is almost equivalent (it also detects interruption). * * @param permits the number of permits to acquire - * - * @return <tt>true</tt> if the permits were acquired and <tt>false</tt> - * otherwise. - * @throws IllegalArgumentException if permits less than zero. + * @return {@code true} if the permits were acquired and + * {@code false} otherwise + * @throws IllegalArgumentException if {@code permits} is negative */ public boolean tryAcquire(int permits) { if (permits < 0) throw new IllegalArgumentException(); @@ -486,55 +501,54 @@ public class Semaphore implements java.io.Serializable { } /** - * Acquires the given number of permits from this semaphore, if all - * become available within the given waiting time and the - * current thread has not been {@link Thread#interrupt interrupted}. - * <p>Acquires the given number of permits, if they are available and - * returns immediately, with the value <tt>true</tt>, + * Acquires the given number of permits from this semaphore, if all + * become available within the given waiting time and the current + * thread has not been {@linkplain Thread#interrupt interrupted}. + * + * <p>Acquires the given number of permits, if they are available and + * returns immediately, with the value {@code true}, * reducing the number of available permits by the given amount. + * * <p>If insufficient permits are available then * the current thread becomes disabled for thread scheduling * purposes and lies dormant until one of three things happens: * <ul> - * <li>Some other thread invokes one of the {@link #release() release} + * <li>Some other thread invokes one of the {@link #release() release} * methods for this semaphore, the current thread is next to be assigned * permits and the number of available permits satisfies this request; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + * <li>Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or * <li>The specified waiting time elapses. * </ul> - * <p>If the permits are acquired then the value <tt>true</tt> is returned. + * + * <p>If the permits are acquired then the value {@code true} is returned. + * * <p>If the current thread: * <ul> * <li>has its interrupted status set on entry to this method; or - * <li>is {@link Thread#interrupt interrupted} while waiting to acquire - * the permits, + * <li>is {@linkplain Thread#interrupt interrupted} while waiting + * to acquire the permits, * </ul> * then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. - * Any permits that were to be assigned to this thread, are instead - * assigned to the next waiting thread(s), as if - * they had been made available by a call to {@link #release()}. - * - * <p>If the specified waiting time elapses then the value <tt>false</tt> - * is returned. - * If the time is - * less than or equal to zero, the method will not wait at all. - * Any permits that were to be assigned to this thread, are instead - * assigned to the next waiting thread(s), as if - * they had been made available by a call to {@link #release()}. + * Any permits that were to be assigned to this thread, are instead + * assigned to other threads trying to acquire permits, as if + * the permits had been made available by a call to {@link #release()}. + * + * <p>If the specified waiting time elapses then the value {@code false} + * is returned. If the time is less than or equal to zero, the method + * will not wait at all. Any permits that were to be assigned to this + * thread, are instead assigned to other threads trying to acquire + * permits, as if the permits had been made available by a call to + * {@link #release()}. * * @param permits the number of permits to acquire * @param timeout the maximum time to wait for the permits - * @param unit the time unit of the <tt>timeout</tt> argument. - * @return <tt>true</tt> if all permits were acquired and <tt>false</tt> - * if the waiting time elapsed before all permits were acquired. - * + * @param unit the time unit of the {@code timeout} argument + * @return {@code true} if all permits were acquired and {@code false} + * if the waiting time elapsed before all permits were acquired * @throws InterruptedException if the current thread is interrupted - * @throws IllegalArgumentException if permits less than zero. - * - * @see Thread#interrupt - * + * @throws IllegalArgumentException if {@code permits} is negative */ public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { @@ -544,19 +558,17 @@ public class Semaphore implements java.io.Serializable { /** * Releases the given number of permits, returning them to the semaphore. - * <p>Releases the given number of permits, increasing the number of + * + * <p>Releases the given number of permits, increasing the number of * available permits by that amount. - * If any threads are blocking trying to acquire permits, then the - * one that has been waiting the longest + * If any threads are trying to acquire permits, then one * is selected and given the permits that were just released. * If the number of available permits satisfies that thread's request - * then that thread is re-enabled for thread scheduling purposes; otherwise - * the thread continues to wait. If there are still permits available - * after the first thread's request has been satisfied, then those permits - * are assigned to the next waiting thread. If it is satisfied then it is - * re-enabled for thread scheduling purposes. This continues until there - * are insufficient permits to satisfy the next waiting thread, or there - * are no more waiting threads. + * then that thread is (re)enabled for thread scheduling purposes; + * otherwise the thread will wait until sufficient permits are available. + * If there are still permits available + * after this thread's request has been satisfied, then those permits + * are assigned in turn to other threads trying to acquire permits. * * <p>There is no requirement that a thread that releases a permit must * have acquired that permit by calling {@link Semaphore#acquire acquire}. @@ -564,7 +576,7 @@ public class Semaphore implements java.io.Serializable { * in the application. * * @param permits the number of permits to release - * @throws IllegalArgumentException if permits less than zero. + * @throws IllegalArgumentException if {@code permits} is negative */ public void release(int permits) { if (permits < 0) throw new IllegalArgumentException(); @@ -573,16 +585,19 @@ public class Semaphore implements java.io.Serializable { /** * Returns the current number of permits available in this semaphore. + * * <p>This method is typically used for debugging and testing purposes. - * @return the number of permits available in this semaphore. + * + * @return the number of permits available in this semaphore */ public int availablePermits() { return sync.getPermits(); } /** - * Acquire and return all permits that are immediately available. - * @return the number of permits + * Acquires and returns all permits that are immediately available. + * + * @return the number of permits acquired */ public int drainPermits() { return sync.drainPermits(); @@ -592,19 +607,21 @@ public class Semaphore implements java.io.Serializable { * Shrinks the number of available permits by the indicated * reduction. This method can be useful in subclasses that use * semaphores to track resources that become unavailable. This - * method differs from <tt>acquire</tt> in that it does not block + * method differs from {@code acquire} in that it does not block * waiting for permits to become available. + * * @param reduction the number of permits to remove - * @throws IllegalArgumentException if reduction is negative + * @throws IllegalArgumentException if {@code reduction} is negative */ protected void reducePermits(int reduction) { - if (reduction < 0) throw new IllegalArgumentException(); + if (reduction < 0) throw new IllegalArgumentException(); sync.reducePermits(reduction); } /** - * Returns true if this semaphore has fairness set true. - * @return true if this semaphore has fairness set true. + * Returns {@code true} if this semaphore has fairness set true. + * + * @return {@code true} if this semaphore has fairness set true */ public boolean isFair() { return sync instanceof FairSync; @@ -612,25 +629,25 @@ public class Semaphore implements java.io.Serializable { /** * Queries whether any threads are waiting to acquire. Note that - * because cancellations may occur at any time, a <tt>true</tt> + * because cancellations may occur at any time, a {@code true} * return does not guarantee that any other thread will ever * acquire. This method is designed primarily for use in * monitoring of the system state. * - * @return true if there may be other threads waiting to acquire - * the lock. + * @return {@code true} if there may be other threads waiting to + * acquire the lock */ - public final boolean hasQueuedThreads() { + public final boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } /** - * Returns an estimate of the number of threads waiting to - * acquire. The value is only an estimate because the number of - * threads may change dynamically while this method traverses - * internal data structures. This method is designed for use in - * monitoring of the system state, not for synchronization - * control. + * Returns an estimate of the number of threads waiting to acquire. + * The value is only an estimate because the number of threads may + * change dynamically while this method traverses internal data + * structures. This method is designed for use in monitoring of the + * system state, not for synchronization control. + * * @return the estimated number of threads waiting for this lock */ public final int getQueueLength() { @@ -638,13 +655,13 @@ public class Semaphore implements java.io.Serializable { } /** - * Returns a collection containing threads that may be waiting to - * acquire. Because the actual set of threads may change - * dynamically while constructing this result, the returned - * collection is only a best-effort estimate. The elements of the - * returned collection are in no particular order. This method is - * designed to facilitate construction of subclasses that provide - * more extensive monitoring facilities. + * Returns a collection containing threads that may be waiting to acquire. + * Because the actual set of threads may change dynamically while + * constructing this result, the returned collection is only a best-effort + * estimate. The elements of the returned collection are in no particular + * order. This method is designed to facilitate construction of + * subclasses that provide more extensive monitoring facilities. + * * @return the collection of threads */ protected Collection<Thread> getQueuedThreads() { @@ -653,13 +670,12 @@ public class Semaphore implements java.io.Serializable { /** * Returns a string identifying this semaphore, as well as its state. - * The state, in brackets, includes the String - * "Permits =" followed by the number of permits. - * @return a string identifying this semaphore, as well as its - * state + * The state, in brackets, includes the String {@code "Permits ="} + * followed by the number of permits. + * + * @return a string identifying this semaphore, as well as its state */ public String toString() { return super.toString() + "[Permits = " + sync.getPermits() + "]"; } - } diff --git a/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java b/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java index b8d3536..0d297ff 100644 --- a/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java +++ b/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java @@ -1,11 +1,13 @@ /* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at + * Written by Doug Lea, Bill Scherer, and Michael Scott 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 java.util.concurrent; import java.util.concurrent.locks.*; +import java.util.concurrent.atomic.*; import java.util.*; // BEGIN android-note @@ -13,20 +15,21 @@ import java.util.*; // END android-note /** - * A {@linkplain BlockingQueue blocking queue} in which each - * <tt>put</tt> must wait for a <tt>take</tt>, and vice versa. A - * synchronous queue does not have any internal capacity, not even a - * capacity of one. You cannot <tt>peek</tt> at a synchronous queue - * because an element is only present when you try to take it; you - * cannot add an element (using any method) unless another thread is - * trying to remove it; you cannot iterate as there is nothing to - * iterate. The <em>head</em> of the queue is the element that the - * first queued thread is trying to add to the queue; if there are no - * queued threads then no element is being added and the head is - * <tt>null</tt>. For purposes of other <tt>Collection</tt> methods - * (for example <tt>contains</tt>), a <tt>SynchronousQueue</tt> acts - * as an empty collection. This queue does not permit <tt>null</tt> - * elements. + * A {@linkplain BlockingQueue blocking queue} in which each insert + * operation must wait for a corresponding remove operation by another + * thread, and vice versa. A synchronous queue does not have any + * internal capacity, not even a capacity of one. You cannot + * <tt>peek</tt> at a synchronous queue because an element is only + * present when you try to remove it; you cannot insert an element + * (using any method) unless another thread is trying to remove it; + * you cannot iterate as there is nothing to iterate. The + * <em>head</em> of the queue is the element that the first queued + * inserting thread is trying to add to the queue; if there is no such + * queued thread then no element is available for removal and + * <tt>poll()</tt> will return <tt>null</tt>. For purposes of other + * <tt>Collection</tt> methods (for example <tt>contains</tt>), a + * <tt>SynchronousQueue</tt> acts as an empty collection. This queue + * does not permit <tt>null</tt> elements. * * <p>Synchronous queues are similar to rendezvous channels used in * CSP and Ada. They are well suited for handoff designs, in which an @@ -37,449 +40,841 @@ import java.util.*; * <p> This class supports an optional fairness policy for ordering * waiting producer and consumer threads. By default, this ordering * is not guaranteed. However, a queue constructed with fairness set - * to <tt>true</tt> grants threads access in FIFO order. Fairness - * generally decreases throughput but reduces variability and avoids - * starvation. + * to <tt>true</tt> grants threads access in FIFO order. * - * <p>This class implements all of the <em>optional</em> methods - * of the {@link Collection} and {@link Iterator} interfaces. + * <p>This class and its iterator implement all of the + * <em>optional</em> methods of the {@link Collection} and {@link + * Iterator} interfaces. * * @since 1.5 - * @author Doug Lea + * @author Doug Lea and Bill Scherer and Michael Scott * @param <E> the type of elements held in this collection */ public class SynchronousQueue<E> extends AbstractQueue<E> - implements BlockingQueue<E>, java.io.Serializable { + implements BlockingQueue<E>, java.io.Serializable { private static final long serialVersionUID = -3223113410248163686L; /* - This implementation divides actions into two cases for puts: - - * An arriving producer that does not already have a waiting consumer - creates a node holding item, and then waits for a consumer to take it. - * An arriving producer that does already have a waiting consumer fills - the slot node created by the consumer, and notifies it to continue. - - And symmetrically, two for takes: - - * An arriving consumer that does not already have a waiting producer - creates an empty slot node, and then waits for a producer to fill it. - * An arriving consumer that does already have a waiting producer takes - item from the node created by the producer, and notifies it to continue. - - When a put or take waiting for the actions of its counterpart - aborts due to interruption or timeout, it marks the node - it created as "CANCELLED", which causes its counterpart to retry - the entire put or take sequence. - - This requires keeping two simple queues, waitingProducers and - waitingConsumers. Each of these can be FIFO (preserves fairness) - or LIFO (improves throughput). - */ - - /** Lock protecting both wait queues */ - private final ReentrantLock qlock; - /** Queue holding waiting puts */ - private final WaitQueue waitingProducers; - /** Queue holding waiting takes */ - private final WaitQueue waitingConsumers; + * This class implements extensions of the dual stack and dual + * queue algorithms described in "Nonblocking Concurrent Objects + * with Condition Synchronization", by W. N. Scherer III and + * M. L. Scott. 18th Annual Conf. on Distributed Computing, + * Oct. 2004 (see also + * http://www.cs.rochester.edu/u/scott/synchronization/pseudocode/duals.html). + * The (Lifo) stack is used for non-fair mode, and the (Fifo) + * queue for fair mode. The performance of the two is generally + * similar. Fifo usually supports higher throughput under + * contention but Lifo maintains higher thread locality in common + * applications. + * + * A dual queue (and similarly stack) is one that at any given + * time either holds "data" -- items provided by put operations, + * or "requests" -- slots representing take operations, or is + * empty. A call to "fulfill" (i.e., a call requesting an item + * from a queue holding data or vice versa) dequeues a + * complementary node. The most interesting feature of these + * queues is that any operation can figure out which mode the + * queue is in, and act accordingly without needing locks. + * + * Both the queue and stack extend abstract class Transferer + * defining the single method transfer that does a put or a + * take. These are unified into a single method because in dual + * data structures, the put and take operations are symmetrical, + * so nearly all code can be combined. The resulting transfer + * methods are on the long side, but are easier to follow than + * they would be if broken up into nearly-duplicated parts. + * + * The queue and stack data structures share many conceptual + * similarities but very few concrete details. For simplicity, + * they are kept distinct so that they can later evolve + * separately. + * + * The algorithms here differ from the versions in the above paper + * in extending them for use in synchronous queues, as well as + * dealing with cancellation. The main differences include: + * + * 1. The original algorithms used bit-marked pointers, but + * the ones here use mode bits in nodes, leading to a number + * of further adaptations. + * 2. SynchronousQueues must block threads waiting to become + * fulfilled. + * 3. Support for cancellation via timeout and interrupts, + * including cleaning out cancelled nodes/threads + * from lists to avoid garbage retention and memory depletion. + * + * Blocking is mainly accomplished using LockSupport park/unpark, + * except that nodes that appear to be the next ones to become + * fulfilled first spin a bit (on multiprocessors only). On very + * busy synchronous queues, spinning can dramatically improve + * throughput. And on less busy ones, the amount of spinning is + * small enough not to be noticeable. + * + * Cleaning is done in different ways in queues vs stacks. For + * queues, we can almost always remove a node immediately in O(1) + * time (modulo retries for consistency checks) when it is + * cancelled. But if it may be pinned as the current tail, it must + * wait until some subsequent cancellation. For stacks, we need a + * potentially O(n) traversal to be sure that we can remove the + * node, but this can run concurrently with other threads + * accessing the stack. + * + * While garbage collection takes care of most node reclamation + * issues that otherwise complicate nonblocking algorithms, care + * is taken to "forget" references to data, other nodes, and + * threads that might be held on to long-term by blocked + * threads. In cases where setting to null would otherwise + * conflict with main algorithms, this is done by changing a + * node's link to now point to the node itself. This doesn't arise + * much for Stack nodes (because blocked threads do not hang on to + * old head pointers), but references in Queue nodes must be + * aggressively forgotten to avoid reachability of everything any + * node has ever referred to since arrival. + */ /** - * Creates a <tt>SynchronousQueue</tt> with nonfair access policy. + * Shared internal API for dual stacks and queues. */ - public SynchronousQueue() { - this(false); + static abstract class Transferer { + /** + * Performs a put or take. + * + * @param e if non-null, the item to be handed to a consumer; + * if null, requests that transfer return an item + * offered by producer. + * @param timed if this operation should timeout + * @param nanos the timeout, in nanoseconds + * @return if non-null, the item provided or received; if null, + * the operation failed due to timeout or interrupt -- + * the caller can distinguish which of these occurred + * by checking Thread.interrupted. + */ + abstract Object transfer(Object e, boolean timed, long nanos); } + /** The number of CPUs, for spin control */ + static final int NCPUS = Runtime.getRuntime().availableProcessors(); + /** - * Creates a <tt>SynchronousQueue</tt> with specified fairness policy. - * @param fair if true, threads contend in FIFO order for access; - * otherwise the order is unspecified. + * The number of times to spin before blocking in timed waits. + * The value is empirically derived -- it works well across a + * variety of processors and OSes. Empirically, the best value + * seems not to vary with number of CPUs (beyond 2) so is just + * a constant. */ - public SynchronousQueue(boolean fair) { - if (fair) { - qlock = new ReentrantLock(true); - waitingProducers = new FifoWaitQueue(); - waitingConsumers = new FifoWaitQueue(); - } - else { - qlock = new ReentrantLock(); - waitingProducers = new LifoWaitQueue(); - waitingConsumers = new LifoWaitQueue(); - } - } + static final int maxTimedSpins = (NCPUS < 2)? 0 : 32; /** - * Queue to hold waiting puts/takes; specialized to Fifo/Lifo below. - * These queues have all transient fields, but are serializable - * in order to recover fairness settings when deserialized. + * The number of times to spin before blocking in untimed waits. + * This is greater than timed value because untimed waits spin + * faster since they don't need to check times on each spin. */ - static abstract class WaitQueue implements java.io.Serializable { - /** Create, add, and return node for x */ - abstract Node enq(Object x); - /** Remove and return node, or null if empty */ - abstract Node deq(); - } + static final int maxUntimedSpins = maxTimedSpins * 16; /** - * FIFO queue to hold waiting puts/takes. + * The number of nanoseconds for which it is faster to spin + * rather than to use timed park. A rough estimate suffices. */ - static final class FifoWaitQueue extends WaitQueue implements java.io.Serializable { - private static final long serialVersionUID = -3623113410248163686L; - private transient Node head; - private transient Node last; - - Node enq(Object x) { - Node p = new Node(x); - if (last == null) - last = head = p; - else - last = last.next = p; - return p; - } + static final long spinForTimeoutThreshold = 1000L; + + /** Dual stack */ + static final class TransferStack extends Transferer { + /* + * This extends Scherer-Scott dual stack algorithm, differing, + * among other ways, by using "covering" nodes rather than + * bit-marked pointers: Fulfilling operations push on marker + * nodes (with FULFILLING bit set in mode) to reserve a spot + * to match a waiting node. + */ - Node deq() { - Node p = head; - if (p != null) { - if ((head = p.next) == null) - last = null; - p.next = null; + /* Modes for SNodes, ORed together in node fields */ + /** Node represents an unfulfilled consumer */ + static final int REQUEST = 0; + /** Node represents an unfulfilled producer */ + static final int DATA = 1; + /** Node is fulfilling another unfulfilled DATA or REQUEST */ + static final int FULFILLING = 2; + + /** Return true if m has fulfilling bit set */ + static boolean isFulfilling(int m) { return (m & FULFILLING) != 0; } + + /** Node class for TransferStacks. */ + static final class SNode { + volatile SNode next; // next node in stack + volatile SNode match; // the node matched to this + volatile Thread waiter; // to control park/unpark + Object item; // data; or null for REQUESTs + int mode; + // Note: item and mode fields don't need to be volatile + // since they are always written before, and read after, + // other volatile/atomic operations. + + SNode(Object item) { + this.item = item; } - return p; - } - } - /** - * LIFO queue to hold waiting puts/takes. - */ - static final class LifoWaitQueue extends WaitQueue implements java.io.Serializable { - private static final long serialVersionUID = -3633113410248163686L; - private transient Node head; + static final AtomicReferenceFieldUpdater<SNode, SNode> + nextUpdater = AtomicReferenceFieldUpdater.newUpdater + (SNode.class, SNode.class, "next"); - Node enq(Object x) { - return head = new Node(x, head); - } + boolean casNext(SNode cmp, SNode val) { + return (cmp == next && + nextUpdater.compareAndSet(this, cmp, val)); + } + + static final AtomicReferenceFieldUpdater<SNode, SNode> + matchUpdater = AtomicReferenceFieldUpdater.newUpdater + (SNode.class, SNode.class, "match"); + + /** + * Tries to match node s to this node, if so, waking up thread. + * Fulfillers call tryMatch to identify their waiters. + * Waiters block until they have been matched. + * + * @param s the node to match + * @return true if successfully matched to s + */ + boolean tryMatch(SNode s) { + if (match == null && + matchUpdater.compareAndSet(this, null, s)) { + Thread w = waiter; + if (w != null) { // waiters need at most one unpark + waiter = null; + LockSupport.unpark(w); + } + return true; + } + return match == s; + } + + /** + * Tries to cancel a wait by matching node to itself. + */ + void tryCancel() { + matchUpdater.compareAndSet(this, null, this); + } - Node deq() { - Node p = head; - if (p != null) { - head = p.next; - p.next = null; + boolean isCancelled() { + return match == this; } - return p; } - } - /** - * Nodes each maintain an item and handle waits and signals for - * getting and setting it. The class extends - * AbstractQueuedSynchronizer to manage blocking, using AQS state - * 0 for waiting, 1 for ack, -1 for cancelled. - */ - static final class Node extends AbstractQueuedSynchronizer { - /** Synchronization state value representing that node acked */ - private static final int ACK = 1; - /** Synchronization state value representing that node cancelled */ - private static final int CANCEL = -1; + /** The head (top) of the stack */ + volatile SNode head; - /** The item being transferred */ - Object item; - /** Next node in wait queue */ - Node next; + static final AtomicReferenceFieldUpdater<TransferStack, SNode> + headUpdater = AtomicReferenceFieldUpdater.newUpdater + (TransferStack.class, SNode.class, "head"); - /** Creates a node with initial item */ - Node(Object x) { item = x; } + boolean casHead(SNode h, SNode nh) { + return h == head && headUpdater.compareAndSet(this, h, nh); + } - /** Creates a node with initial item and next */ - Node(Object x, Node n) { item = x; next = n; } + /** + * Creates or resets fields of a node. Called only from transfer + * where the node to push on stack is lazily created and + * reused when possible to help reduce intervals between reads + * and CASes of head and to avoid surges of garbage when CASes + * to push nodes fail due to contention. + */ + static SNode snode(SNode s, Object e, SNode next, int mode) { + if (s == null) s = new SNode(e); + s.mode = mode; + s.next = next; + return s; + } /** - * Implements AQS base acquire to succeed if not in WAITING state + * Puts or takes an item. */ - protected boolean tryAcquire(int ignore) { - return getState() != 0; + Object transfer(Object e, boolean timed, long nanos) { + /* + * Basic algorithm is to loop trying one of three actions: + * + * 1. If apparently empty or already containing nodes of same + * mode, try to push node on stack and wait for a match, + * returning it, or null if cancelled. + * + * 2. If apparently containing node of complementary mode, + * try to push a fulfilling node on to stack, match + * with corresponding waiting node, pop both from + * stack, and return matched item. The matching or + * unlinking might not actually be necessary because of + * other threads performing action 3: + * + * 3. If top of stack already holds another fulfilling node, + * help it out by doing its match and/or pop + * operations, and then continue. The code for helping + * is essentially the same as for fulfilling, except + * that it doesn't return the item. + */ + + SNode s = null; // constructed/reused as needed + int mode = (e == null)? REQUEST : DATA; + + for (;;) { + SNode h = head; + if (h == null || h.mode == mode) { // empty or same-mode + if (timed && nanos <= 0) { // can't wait + if (h != null && h.isCancelled()) + casHead(h, h.next); // pop cancelled node + else + return null; + } else if (casHead(h, s = snode(s, e, h, mode))) { + SNode m = awaitFulfill(s, timed, nanos); + if (m == s) { // wait was cancelled + clean(s); + return null; + } + if ((h = head) != null && h.next == s) + casHead(h, s.next); // help s's fulfiller + return mode == REQUEST? m.item : s.item; + } + } else if (!isFulfilling(h.mode)) { // try to fulfill + if (h.isCancelled()) // already cancelled + casHead(h, h.next); // pop and retry + else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) { + for (;;) { // loop until matched or waiters disappear + SNode m = s.next; // m is s's match + if (m == null) { // all waiters are gone + casHead(s, null); // pop fulfill node + s = null; // use new node next time + break; // restart main loop + } + SNode mn = m.next; + if (m.tryMatch(s)) { + casHead(s, mn); // pop both s and m + return (mode == REQUEST)? m.item : s.item; + } else // lost match + s.casNext(m, mn); // help unlink + } + } + } else { // help a fulfiller + SNode m = h.next; // m is h's match + if (m == null) // waiter is gone + casHead(h, null); // pop fulfilling node + else { + SNode mn = m.next; + if (m.tryMatch(h)) // help match + casHead(h, mn); // pop both h and m + else // lost match + h.casNext(m, mn); // help unlink + } + } + } } /** - * Implements AQS base release to signal if state changed + * Spins/blocks until node s is matched by a fulfill operation. + * + * @param s the waiting node + * @param timed true if timed wait + * @param nanos timeout value + * @return matched node, or s if cancelled */ - protected boolean tryRelease(int newState) { - return compareAndSetState(0, newState); + SNode awaitFulfill(SNode s, boolean timed, long nanos) { + /* + * When a node/thread is about to block, it sets its waiter + * field and then rechecks state at least one more time + * before actually parking, thus covering race vs + * fulfiller noticing that waiter is non-null so should be + * woken. + * + * When invoked by nodes that appear at the point of call + * to be at the head of the stack, calls to park are + * preceded by spins to avoid blocking when producers and + * consumers are arriving very close in time. This can + * happen enough to bother only on multiprocessors. + * + * The order of checks for returning out of main loop + * reflects fact that interrupts have precedence over + * normal returns, which have precedence over + * timeouts. (So, on timeout, one last check for match is + * done before giving up.) Except that calls from untimed + * SynchronousQueue.{poll/offer} don't check interrupts + * and don't wait at all, so are trapped in transfer + * method rather than calling awaitFulfill. + */ + long lastTime = (timed)? System.nanoTime() : 0; + Thread w = Thread.currentThread(); + SNode h = head; + int spins = (shouldSpin(s)? + (timed? maxTimedSpins : maxUntimedSpins) : 0); + for (;;) { + if (w.isInterrupted()) + s.tryCancel(); + SNode m = s.match; + if (m != null) + return m; + if (timed) { + long now = System.nanoTime(); + nanos -= now - lastTime; + lastTime = now; + if (nanos <= 0) { + s.tryCancel(); + continue; + } + } + if (spins > 0) + spins = shouldSpin(s)? (spins-1) : 0; + else if (s.waiter == null) + s.waiter = w; // establish waiter so can park next iter + else if (!timed) + LockSupport.park(); + else if (nanos > spinForTimeoutThreshold) + LockSupport.parkNanos(nanos); + } } /** - * Takes item and nulls out field (for sake of GC) + * Returns true if node s is at head or there is an active + * fulfiller. */ - private Object extract() { - Object x = item; - item = null; - return x; + boolean shouldSpin(SNode s) { + SNode h = head; + return (h == s || h == null || isFulfilling(h.mode)); } /** - * Tries to cancel on interrupt; if so rethrowing, - * else setting interrupt state + * Unlinks s from the stack. */ - private void checkCancellationOnInterrupt(InterruptedException ie) - throws InterruptedException { - if (release(CANCEL)) - throw ie; - Thread.currentThread().interrupt(); + void clean(SNode s) { + s.item = null; // forget item + s.waiter = null; // forget thread + + /* + * At worst we may need to traverse entire stack to unlink + * s. If there are multiple concurrent calls to clean, we + * might not see s if another thread has already removed + * it. But we can stop when we see any node known to + * follow s. We use s.next unless it too is cancelled, in + * which case we try the node one past. We don't check any + * further because we don't want to doubly traverse just to + * find sentinel. + */ + + SNode past = s.next; + if (past != null && past.isCancelled()) + past = past.next; + + // Absorb cancelled nodes at head + SNode p; + while ((p = head) != null && p != past && p.isCancelled()) + casHead(p, p.next); + + // Unsplice embedded nodes + while (p != null && p != past) { + SNode n = p.next; + if (n != null && n.isCancelled()) + p.casNext(n, n.next); + else + p = n; + } } + } + + /** Dual Queue */ + static final class TransferQueue extends Transferer { + /* + * This extends Scherer-Scott dual queue algorithm, differing, + * among other ways, by using modes within nodes rather than + * marked pointers. The algorithm is a little simpler than + * that for stacks because fulfillers do not need explicit + * nodes, and matching is done by CAS'ing QNode.item field + * from non-null to null (for put) or vice versa (for take). + */ + + /** Node class for TransferQueue. */ + static final class QNode { + volatile QNode next; // next node in queue + volatile Object item; // CAS'ed to or from null + volatile Thread waiter; // to control park/unpark + final boolean isData; + + QNode(Object item, boolean isData) { + this.item = item; + this.isData = isData; + } + + static final AtomicReferenceFieldUpdater<QNode, QNode> + nextUpdater = AtomicReferenceFieldUpdater.newUpdater + (QNode.class, QNode.class, "next"); + + boolean casNext(QNode cmp, QNode val) { + return (next == cmp && + nextUpdater.compareAndSet(this, cmp, val)); + } + + static final AtomicReferenceFieldUpdater<QNode, Object> + itemUpdater = AtomicReferenceFieldUpdater.newUpdater + (QNode.class, Object.class, "item"); + + boolean casItem(Object cmp, Object val) { + return (item == cmp && + itemUpdater.compareAndSet(this, cmp, val)); + } + + /** + * Tries to cancel by CAS'ing ref to this as item. + */ + void tryCancel(Object cmp) { + itemUpdater.compareAndSet(this, cmp, this); + } + + boolean isCancelled() { + return item == this; + } + + /** + * Returns true if this node is known to be off the queue + * because its next pointer has been forgotten due to + * an advanceHead operation. + */ + boolean isOffList() { + return next == this; + } + } + + /** Head of queue */ + transient volatile QNode head; + /** Tail of queue */ + transient volatile QNode tail; + /** + * Reference to a cancelled node that might not yet have been + * unlinked from queue because it was the last inserted node + * when it cancelled. + */ + transient volatile QNode cleanMe; + + TransferQueue() { + QNode h = new QNode(null, false); // initialize to dummy node. + head = h; + tail = h; + } + + static final AtomicReferenceFieldUpdater<TransferQueue, QNode> + headUpdater = AtomicReferenceFieldUpdater.newUpdater + (TransferQueue.class, QNode.class, "head"); /** - * Fills in the slot created by the consumer and signal consumer to - * continue. + * Tries to cas nh as new head; if successful, unlink + * old head's next node to avoid garbage retention. */ - boolean setItem(Object x) { - item = x; // can place in slot even if cancelled - return release(ACK); + void advanceHead(QNode h, QNode nh) { + if (h == head && headUpdater.compareAndSet(this, h, nh)) + h.next = h; // forget old next } + static final AtomicReferenceFieldUpdater<TransferQueue, QNode> + tailUpdater = AtomicReferenceFieldUpdater.newUpdater + (TransferQueue.class, QNode.class, "tail"); + /** - * Removes item from slot created by producer and signal producer - * to continue. + * Tries to cas nt as new tail. */ - Object getItem() { - return (release(ACK))? extract() : null; + void advanceTail(QNode t, QNode nt) { + if (tail == t) + tailUpdater.compareAndSet(this, t, nt); } + static final AtomicReferenceFieldUpdater<TransferQueue, QNode> + cleanMeUpdater = AtomicReferenceFieldUpdater.newUpdater + (TransferQueue.class, QNode.class, "cleanMe"); + /** - * Waits for a consumer to take item placed by producer. + * Tries to CAS cleanMe slot. */ - void waitForTake() throws InterruptedException { - try { - acquireInterruptibly(0); - } catch (InterruptedException ie) { - checkCancellationOnInterrupt(ie); - } + boolean casCleanMe(QNode cmp, QNode val) { + return (cleanMe == cmp && + cleanMeUpdater.compareAndSet(this, cmp, val)); } /** - * Waits for a producer to put item placed by consumer. + * Puts or takes an item. */ - Object waitForPut() throws InterruptedException { - try { - acquireInterruptibly(0); - } catch (InterruptedException ie) { - checkCancellationOnInterrupt(ie); + Object transfer(Object e, boolean timed, long nanos) { + /* Basic algorithm is to loop trying to take either of + * two actions: + * + * 1. If queue apparently empty or holding same-mode nodes, + * try to add node to queue of waiters, wait to be + * fulfilled (or cancelled) and return matching item. + * + * 2. If queue apparently contains waiting items, and this + * call is of complementary mode, try to fulfill by CAS'ing + * item field of waiting node and dequeuing it, and then + * returning matching item. + * + * In each case, along the way, check for and try to help + * advance head and tail on behalf of other stalled/slow + * threads. + * + * The loop starts off with a null check guarding against + * seeing uninitialized head or tail values. This never + * happens in current SynchronousQueue, but could if + * callers held non-volatile/final ref to the + * transferer. The check is here anyway because it places + * null checks at top of loop, which is usually faster + * than having them implicitly interspersed. + */ + + QNode s = null; // constructed/reused as needed + boolean isData = (e != null); + + for (;;) { + QNode t = tail; + QNode h = head; + if (t == null || h == null) // saw uninitialized value + continue; // spin + + if (h == t || t.isData == isData) { // empty or same-mode + QNode tn = t.next; + if (t != tail) // inconsistent read + continue; + if (tn != null) { // lagging tail + advanceTail(t, tn); + continue; + } + if (timed && nanos <= 0) // can't wait + return null; + if (s == null) + s = new QNode(e, isData); + if (!t.casNext(null, s)) // failed to link in + continue; + + advanceTail(t, s); // swing tail and wait + Object x = awaitFulfill(s, e, timed, nanos); + if (x == s) { // wait was cancelled + clean(t, s); + return null; + } + + if (!s.isOffList()) { // not already unlinked + advanceHead(t, s); // unlink if head + if (x != null) // and forget fields + s.item = s; + s.waiter = null; + } + return (x != null)? x : e; + + } else { // complementary-mode + QNode m = h.next; // node to fulfill + if (t != tail || m == null || h != head) + continue; // inconsistent read + + Object x = m.item; + if (isData == (x != null) || // m already fulfilled + x == m || // m cancelled + !m.casItem(x, e)) { // lost CAS + advanceHead(h, m); // dequeue and retry + continue; + } + + advanceHead(h, m); // successfully fulfilled + LockSupport.unpark(m.waiter); + return (x != null)? x : e; + } } - return extract(); } /** - * Waits for a consumer to take item placed by producer or time out. + * Spins/blocks until node s is fulfilled. + * + * @param s the waiting node + * @param e the comparison value for checking match + * @param timed true if timed wait + * @param nanos timeout value + * @return matched item, or s if cancelled */ - boolean waitForTake(long nanos) throws InterruptedException { - try { - if (!tryAcquireNanos(0, nanos) && - release(CANCEL)) - return false; - } catch (InterruptedException ie) { - checkCancellationOnInterrupt(ie); + Object awaitFulfill(QNode s, Object e, boolean timed, long nanos) { + /* Same idea as TransferStack.awaitFulfill */ + long lastTime = (timed)? System.nanoTime() : 0; + Thread w = Thread.currentThread(); + int spins = ((head.next == s) ? + (timed? maxTimedSpins : maxUntimedSpins) : 0); + for (;;) { + if (w.isInterrupted()) + s.tryCancel(e); + Object x = s.item; + if (x != e) + return x; + if (timed) { + long now = System.nanoTime(); + nanos -= now - lastTime; + lastTime = now; + if (nanos <= 0) { + s.tryCancel(e); + continue; + } + } + if (spins > 0) + --spins; + else if (s.waiter == null) + s.waiter = w; + else if (!timed) + LockSupport.park(); + else if (nanos > spinForTimeoutThreshold) + LockSupport.parkNanos(nanos); } - return true; } /** - * Waits for a producer to put item placed by consumer, or time out. + * Gets rid of cancelled node s with original predecessor pred. */ - Object waitForPut(long nanos) throws InterruptedException { - try { - if (!tryAcquireNanos(0, nanos) && - release(CANCEL)) - return null; - } catch (InterruptedException ie) { - checkCancellationOnInterrupt(ie); + void clean(QNode pred, QNode s) { + s.waiter = null; // forget thread + /* + * At any given time, exactly one node on list cannot be + * deleted -- the last inserted node. To accommodate this, + * if we cannot delete s, we save its predecessor as + * "cleanMe", deleting the previously saved version + * first. At least one of node s or the node previously + * saved can always be deleted, so this always terminates. + */ + while (pred.next == s) { // Return early if already unlinked + QNode h = head; + QNode hn = h.next; // Absorb cancelled first node as head + if (hn != null && hn.isCancelled()) { + advanceHead(h, hn); + continue; + } + QNode t = tail; // Ensure consistent read for tail + if (t == h) + return; + QNode tn = t.next; + if (t != tail) + continue; + if (tn != null) { + advanceTail(t, tn); + continue; + } + if (s != t) { // If not tail, try to unsplice + QNode sn = s.next; + if (sn == s || pred.casNext(s, sn)) + return; + } + QNode dp = cleanMe; + if (dp != null) { // Try unlinking previous cancelled node + QNode d = dp.next; + QNode dn; + if (d == null || // d is gone or + d == dp || // d is off list or + !d.isCancelled() || // d not cancelled or + (d != t && // d not tail and + (dn = d.next) != null && // has successor + dn != d && // that is on list + dp.casNext(d, dn))) // d unspliced + casCleanMe(dp, null); + if (dp == pred) + return; // s is already saved node + } else if (casCleanMe(null, pred)) + return; // Postpone cleaning s } - return extract(); } } /** + * The transferer. Set only in constructor, but cannot be declared + * as final without further complicating serialization. Since + * this is accessed only at most once per public method, there + * isn't a noticeable performance penalty for using volatile + * instead of final here. + */ + private transient volatile Transferer transferer; + + /** + * Creates a <tt>SynchronousQueue</tt> with nonfair access policy. + */ + public SynchronousQueue() { + this(false); + } + + /** + * Creates a <tt>SynchronousQueue</tt> with the specified fairness policy. + * + * @param fair if true, waiting threads contend in FIFO order for + * access; otherwise the order is unspecified. + */ + public SynchronousQueue(boolean fair) { + transferer = (fair)? new TransferQueue() : new TransferStack(); + } + + /** * Adds the specified element to this queue, waiting if necessary for * another thread to receive it. - * @param o the element to add - * @throws InterruptedException if interrupted while waiting. - * @throws NullPointerException if the specified element is <tt>null</tt>. + * + * @throws InterruptedException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} */ public void put(E o) throws InterruptedException { if (o == null) throw new NullPointerException(); - final ReentrantLock qlock = this.qlock; - - for (;;) { - Node node; - boolean mustWait; - if (Thread.interrupted()) throw new InterruptedException(); - qlock.lock(); - try { - node = waitingConsumers.deq(); - if ( (mustWait = (node == null)) ) - node = waitingProducers.enq(o); - } finally { - qlock.unlock(); - } - - if (mustWait) { - node.waitForTake(); - return; - } - - else if (node.setItem(o)) - return; - - // else consumer cancelled, so retry + if (transferer.transfer(o, false, 0) == null) { + Thread.interrupted(); + throw new InterruptedException(); } } /** * Inserts the specified element into this queue, waiting if necessary * up to the specified wait time for another thread to receive it. - * @param o the element to add - * @param timeout how long to wait before giving up, in units of - * <tt>unit</tt> - * @param unit a <tt>TimeUnit</tt> determining how to interpret the - * <tt>timeout</tt> parameter - * @return <tt>true</tt> if successful, or <tt>false</tt> if - * the specified waiting time elapses before a consumer appears. - * @throws InterruptedException if interrupted while waiting. - * @throws NullPointerException if the specified element is <tt>null</tt>. + * + * @return <tt>true</tt> if successful, or <tt>false</tt> if the + * specified waiting time elapses before a consumer appears. + * @throws InterruptedException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} */ - public boolean offer(E o, long timeout, TimeUnit unit) throws InterruptedException { + public boolean offer(E o, long timeout, TimeUnit unit) + throws InterruptedException { if (o == null) throw new NullPointerException(); - long nanos = unit.toNanos(timeout); - final ReentrantLock qlock = this.qlock; - for (;;) { - Node node; - boolean mustWait; - if (Thread.interrupted()) throw new InterruptedException(); - qlock.lock(); - try { - node = waitingConsumers.deq(); - if ( (mustWait = (node == null)) ) - node = waitingProducers.enq(o); - } finally { - qlock.unlock(); - } - - if (mustWait) - return node.waitForTake(nanos); - - else if (node.setItem(o)) - return true; + if (transferer.transfer(o, true, unit.toNanos(timeout)) != null) + return true; + if (!Thread.interrupted()) + return false; + throw new InterruptedException(); + } - // else consumer cancelled, so retry - } + /** + * Inserts the specified element into this queue, if another thread is + * waiting to receive it. + * + * @param e the element to add + * @return <tt>true</tt> if the element was added to this queue, else + * <tt>false</tt> + * @throws NullPointerException if the specified element is null + */ + public boolean offer(E e) { + if (e == null) throw new NullPointerException(); + return transferer.transfer(e, true, 0) != null; } /** * Retrieves and removes the head of this queue, waiting if necessary * for another thread to insert it. - * @throws InterruptedException if interrupted while waiting. + * * @return the head of this queue + * @throws InterruptedException {@inheritDoc} */ public E take() throws InterruptedException { - final ReentrantLock qlock = this.qlock; - for (;;) { - Node node; - boolean mustWait; - - if (Thread.interrupted()) throw new InterruptedException(); - qlock.lock(); - try { - node = waitingProducers.deq(); - if ( (mustWait = (node == null)) ) - node = waitingConsumers.enq(null); - } finally { - qlock.unlock(); - } - - if (mustWait) { - Object x = node.waitForPut(); - return (E)x; - } - else { - Object x = node.getItem(); - if (x != null) - return (E)x; - // else cancelled, so retry - } - } + Object e = transferer.transfer(null, false, 0); + if (e != null) + return (E)e; + Thread.interrupted(); + throw new InterruptedException(); } /** * Retrieves and removes the head of this queue, waiting * if necessary up to the specified wait time, for another thread * to insert it. - * @param timeout how long to wait before giving up, in units of - * <tt>unit</tt> - * @param unit a <tt>TimeUnit</tt> determining how to interpret the - * <tt>timeout</tt> parameter + * * @return the head of this queue, or <tt>null</tt> if the - * specified waiting time elapses before an element is present. - * @throws InterruptedException if interrupted while waiting. + * specified waiting time elapses before an element is present. + * @throws InterruptedException {@inheritDoc} */ public E poll(long timeout, TimeUnit unit) throws InterruptedException { - long nanos = unit.toNanos(timeout); - final ReentrantLock qlock = this.qlock; - - for (;;) { - Node node; - boolean mustWait; - - if (Thread.interrupted()) throw new InterruptedException(); - qlock.lock(); - try { - node = waitingProducers.deq(); - if ( (mustWait = (node == null)) ) - node = waitingConsumers.enq(null); - } finally { - qlock.unlock(); - } - - if (mustWait) { - Object x = node.waitForPut(nanos); - return (E)x; - } - else { - Object x = node.getItem(); - if (x != null) - return (E)x; - // else cancelled, so retry - } - } - } - - // Untimed nonblocking versions - - /** - * Inserts the specified element into this queue, if another thread is - * waiting to receive it. - * - * @param o the element to add. - * @return <tt>true</tt> if it was possible to add the element to - * this queue, else <tt>false</tt> - * @throws NullPointerException if the specified element is <tt>null</tt> - */ - public boolean offer(E o) { - if (o == null) throw new NullPointerException(); - final ReentrantLock qlock = this.qlock; - - for (;;) { - Node node; - qlock.lock(); - try { - node = waitingConsumers.deq(); - } finally { - qlock.unlock(); - } - if (node == null) - return false; - - else if (node.setItem(o)) - return true; - // else retry - } + Object e = transferer.transfer(null, true, unit.toNanos(timeout)); + if (e != null || !Thread.interrupted()) + return (E)e; + throw new InterruptedException(); } /** @@ -490,30 +885,13 @@ public class SynchronousQueue<E> extends AbstractQueue<E> * element is available. */ public E poll() { - final ReentrantLock qlock = this.qlock; - for (;;) { - Node node; - qlock.lock(); - try { - node = waitingProducers.deq(); - } finally { - qlock.unlock(); - } - if (node == null) - return null; - - else { - Object x = node.getItem(); - if (x != null) - return (E)x; - // else retry - } - } + return (E)transferer.transfer(null, true, 0); } /** - * Always returns <tt>true</tt>. + * Always returns <tt>true</tt>. * A <tt>SynchronousQueue</tt> has no internal capacity. + * * @return <tt>true</tt> */ public boolean isEmpty() { @@ -523,6 +901,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E> /** * Always returns zero. * A <tt>SynchronousQueue</tt> has no internal capacity. + * * @return zero. */ public int size() { @@ -532,6 +911,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E> /** * Always returns zero. * A <tt>SynchronousQueue</tt> has no internal capacity. + * * @return zero. */ public int remainingCapacity() { @@ -542,11 +922,13 @@ public class SynchronousQueue<E> extends AbstractQueue<E> * Does nothing. * A <tt>SynchronousQueue</tt> has no internal capacity. */ - public void clear() {} + public void clear() { + } /** * Always returns <tt>false</tt>. * A <tt>SynchronousQueue</tt> has no internal capacity. + * * @param o the element * @return <tt>false</tt> */ @@ -566,8 +948,9 @@ public class SynchronousQueue<E> extends AbstractQueue<E> } /** - * Returns <tt>false</tt> unless given collection is empty. + * Returns <tt>false</tt> unless the given collection is empty. * A <tt>SynchronousQueue</tt> has no internal capacity. + * * @param c the collection * @return <tt>false</tt> unless given collection is empty */ @@ -578,6 +961,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E> /** * Always returns <tt>false</tt>. * A <tt>SynchronousQueue</tt> has no internal capacity. + * * @param c the collection * @return <tt>false</tt> */ @@ -588,6 +972,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E> /** * Always returns <tt>false</tt>. * A <tt>SynchronousQueue</tt> has no internal capacity. + * * @param c the collection * @return <tt>false</tt> */ @@ -596,9 +981,10 @@ public class SynchronousQueue<E> extends AbstractQueue<E> } /** - * Always returns <tt>null</tt>. + * Always returns <tt>null</tt>. * A <tt>SynchronousQueue</tt> does not return elements * unless actively waited on. + * * @return <tt>null</tt> */ public E peek() { @@ -628,7 +1014,6 @@ public class SynchronousQueue<E> extends AbstractQueue<E> return new EmptyIterator<E>(); } - /** * Returns a zero-length array. * @return a zero-length array @@ -640,8 +1025,10 @@ public class SynchronousQueue<E> extends AbstractQueue<E> /** * Sets the zeroeth element of the specified array to <tt>null</tt> * (if the array has non-zero length) and returns it. + * * @param a the array * @return the specified array + * @throws NullPointerException if the specified array is null */ public <T> T[] toArray(T[] a) { if (a.length > 0) @@ -649,7 +1036,12 @@ public class SynchronousQueue<E> extends AbstractQueue<E> return a; } - + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection<? super E> c) { if (c == null) throw new NullPointerException(); @@ -664,6 +1056,12 @@ public class SynchronousQueue<E> extends AbstractQueue<E> return n; } + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection<? super E> c, int maxElements) { if (c == null) throw new NullPointerException(); @@ -677,9 +1075,54 @@ public class SynchronousQueue<E> extends AbstractQueue<E> } return n; } -} + /* + * To cope with serialization strategy in the 1.5 version of + * SynchronousQueue, we declare some unused classes and fields + * that exist solely to enable serializability across versions. + * These fields are never used, so are initialized only if this + * object is ever serialized or deserialized. + */ + static class WaitQueue implements java.io.Serializable { } + static class LifoWaitQueue extends WaitQueue { + private static final long serialVersionUID = -3633113410248163686L; + } + static class FifoWaitQueue extends WaitQueue { + private static final long serialVersionUID = -3623113410248163686L; + } + private ReentrantLock qlock; + private WaitQueue waitingProducers; + private WaitQueue waitingConsumers; + /** + * Save the state to a stream (that is, serialize it). + * + * @param s the stream + */ + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + boolean fair = transferer instanceof TransferQueue; + if (fair) { + qlock = new ReentrantLock(true); + waitingProducers = new FifoWaitQueue(); + waitingConsumers = new FifoWaitQueue(); + } + else { + qlock = new ReentrantLock(); + waitingProducers = new LifoWaitQueue(); + waitingConsumers = new LifoWaitQueue(); + } + s.defaultWriteObject(); + } + private void readObject(final java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + if (waitingProducers instanceof FifoWaitQueue) + transferer = new TransferQueue(); + else + transferer = new TransferStack(); + } +} diff --git a/concurrent/src/main/java/java/util/concurrent/ThreadFactory.java b/concurrent/src/main/java/java/util/concurrent/ThreadFactory.java index 04c63e1..2f0fb1a 100644 --- a/concurrent/src/main/java/java/util/concurrent/ThreadFactory.java +++ b/concurrent/src/main/java/java/util/concurrent/ThreadFactory.java @@ -11,7 +11,7 @@ package java.util.concurrent; * removes hardwiring of calls to {@link Thread#Thread(Runnable) new Thread}, * enabling applications to use special thread subclasses, priorities, etc. * - * <p> + * <p> * The simplest implementation of this interface is just: * <pre> * class SimpleThreadFactory implements ThreadFactory { @@ -23,18 +23,19 @@ package java.util.concurrent; * * The {@link Executors#defaultThreadFactory} method provides a more * useful simple implementation, that sets the created thread context - * to known values before returning it. + * to known values before returning it. * @since 1.5 * @author Doug Lea */ -public interface ThreadFactory { +public interface ThreadFactory { /** - * Constructs a new <tt>Thread</tt>. Implementations may also initialize - * priority, name, daemon status, <tt>ThreadGroup</tt>, etc. + * Constructs a new {@code Thread}. Implementations may also initialize + * priority, name, daemon status, {@code ThreadGroup}, etc. * * @param r a runnable to be executed by new thread instance - * @return constructed thread + * @return constructed thread, or {@code null} if the request to + * create a thread is rejected */ Thread newThread(Runnable r); } diff --git a/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java b/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java index 1aa8e55..d0c934d 100644 --- a/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java +++ b/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java @@ -6,13 +6,9 @@ package java.util.concurrent; import java.util.concurrent.locks.*; +import java.util.concurrent.atomic.*; import java.util.*; -// BEGIN android-note -// Altered {@link TimeUnit#NANOSECONDS} because our javadoc tool doesn't -// like it. -// END android-note - /** * An {@link ExecutorService} that executes each submitted task using * one of possibly several pooled threads, normally configured @@ -23,7 +19,7 @@ import java.util.*; * asynchronous tasks, due to reduced per-task invocation overhead, * and they provide a means of bounding and managing the resources, * including threads, consumed when executing a collection of tasks. - * Each <tt>ThreadPoolExecutor</tt> also maintains some basic + * Each {@code ThreadPoolExecutor} also maintains some basic * statistics, such as the number of completed tasks. * * <p>To be useful across a wide range of contexts, this class @@ -42,61 +38,63 @@ import java.util.*; * * <dt>Core and maximum pool sizes</dt> * - * <dd>A <tt>ThreadPoolExecutor</tt> will automatically adjust the - * pool size - * (see {@link ThreadPoolExecutor#getPoolSize}) - * according to the bounds set by corePoolSize - * (see {@link ThreadPoolExecutor#getCorePoolSize}) - * and - * maximumPoolSize - * (see {@link ThreadPoolExecutor#getMaximumPoolSize}). - * When a new task is submitted in method {@link - * ThreadPoolExecutor#execute}, and fewer than corePoolSize threads - * are running, a new thread is created to handle the request, even if - * other worker threads are idle. If there are more than - * corePoolSize but less than maximumPoolSize threads running, a new - * thread will be created only if the queue is full. By setting - * corePoolSize and maximumPoolSize the same, you create a fixed-size - * thread pool. By setting maximumPoolSize to an essentially unbounded - * value such as <tt>Integer.MAX_VALUE</tt>, you allow the pool to - * accommodate an arbitrary number of concurrent tasks. Most typically, - * core and maximum pool sizes are set only upon construction, but they - * may also be changed dynamically using {@link - * ThreadPoolExecutor#setCorePoolSize} and {@link - * ThreadPoolExecutor#setMaximumPoolSize}. <dd> + * <dd>A {@code ThreadPoolExecutor} will automatically adjust the + * pool size (see {@link #getPoolSize}) + * according to the bounds set by + * corePoolSize (see {@link #getCorePoolSize}) and + * maximumPoolSize (see {@link #getMaximumPoolSize}). + * + * When a new task is submitted in method {@link #execute}, and fewer + * than corePoolSize threads are running, a new thread is created to + * handle the request, even if other worker threads are idle. If + * there are more than corePoolSize but less than maximumPoolSize + * threads running, a new thread will be created only if the queue is + * full. By setting corePoolSize and maximumPoolSize the same, you + * create a fixed-size thread pool. By setting maximumPoolSize to an + * essentially unbounded value such as {@code Integer.MAX_VALUE}, you + * allow the pool to accommodate an arbitrary number of concurrent + * tasks. Most typically, core and maximum pool sizes are set only + * upon construction, but they may also be changed dynamically using + * {@link #setCorePoolSize} and {@link #setMaximumPoolSize}. </dd> * - * <dt> On-demand construction + * <dt>On-demand construction</dt> * * <dd> By default, even core threads are initially created and - * started only when needed by new tasks, but this can be overridden - * dynamically using method {@link - * ThreadPoolExecutor#prestartCoreThread} or - * {@link ThreadPoolExecutor#prestartAllCoreThreads}. </dd> + * started only when new tasks arrive, but this can be overridden + * dynamically using method {@link #prestartCoreThread} or {@link + * #prestartAllCoreThreads}. You probably want to prestart threads if + * you construct the pool with a non-empty queue. </dd> * * <dt>Creating new threads</dt> * - * <dd>New threads are created using a {@link - * java.util.concurrent.ThreadFactory}. If not otherwise specified, a - * {@link Executors#defaultThreadFactory} is used, that creates threads to all - * be in the same {@link ThreadGroup} and with the same - * <tt>NORM_PRIORITY</tt> priority and non-daemon status. By supplying - * a different ThreadFactory, you can alter the thread's name, thread - * group, priority, daemon status, etc. </dd> + * <dd>New threads are created using a {@link ThreadFactory}. If not + * otherwise specified, a {@link Executors#defaultThreadFactory} is + * used, that creates threads to all be in the same {@link + * ThreadGroup} and with the same {@code NORM_PRIORITY} priority and + * non-daemon status. By supplying a different ThreadFactory, you can + * alter the thread's name, thread group, priority, daemon status, + * etc. If a {@code ThreadFactory} fails to create a thread when asked + * by returning null from {@code newThread}, the executor will + * continue, but might not be able to execute any tasks. Threads + * should possess the "modifyThread" {@code RuntimePermission}. If + * worker threads or other threads using the pool do not possess this + * permission, service may be degraded: configuration changes may not + * take effect in a timely manner, and a shutdown pool may remain in a + * state in which termination is possible but not completed.</dd> * * <dt>Keep-alive times</dt> * * <dd>If the pool currently has more than corePoolSize threads, * excess threads will be terminated if they have been idle for more - * than the keepAliveTime (see {@link - * ThreadPoolExecutor#getKeepAliveTime}). This provides a means of - * reducing resource consumption when the pool is not being actively - * used. If the pool becomes more active later, new threads will be - * constructed. This parameter can also be changed dynamically - * using method {@link ThreadPoolExecutor#setKeepAliveTime}. Using - * a value of <tt>Long.MAX_VALUE</tt> <code>TimeUnit.NANOSECONDS</code> - * effectively disables idle threads from ever terminating prior - * to shut down. - * </dd> + * than the keepAliveTime (see {@link #getKeepAliveTime}). This + * provides a means of reducing resource consumption when the pool is + * not being actively used. If the pool becomes more active later, new + * threads will be constructed. This parameter can also be changed + * dynamically using method {@link #setKeepAliveTime}. Using a value + * of {@code Long.MAX_VALUE} {@link TimeUnit#NANOSECONDS} effectively + * disables idle threads from ever terminating prior to shut down. The + * keep-alive policy applies only when there are more than + * corePoolSizeThreads.</dd> * * <dt>Queuing</dt> * @@ -112,7 +110,7 @@ import java.util.*; * <li> If corePoolSize or more threads are running, the Executor * always prefers queuing a request rather than adding a new * thread.</li> - * + * * <li> If a request cannot be queued, a new thread is created unless * this would exceed maximumPoolSize, in which case, the task will be * rejected.</li> @@ -135,7 +133,7 @@ import java.util.*; * * <li><em> Unbounded queues.</em> Using an unbounded queue (for * example a {@link LinkedBlockingQueue} without a predefined - * capacity) will cause new tasks to be queued in cases where all + * capacity) will cause new tasks to wait in the queue when all * corePoolSize threads are busy. Thus, no more than corePoolSize * threads will ever be created. (And the value of the maximumPoolSize * therefore doesn't have any effect.) This may be appropriate when @@ -165,35 +163,33 @@ import java.util.*; * * <dt>Rejected tasks</dt> * - * <dd> New tasks submitted in method {@link - * ThreadPoolExecutor#execute} will be <em>rejected</em> when the - * Executor has been shut down, and also when the Executor uses finite - * bounds for both maximum threads and work queue capacity, and is - * saturated. In either case, the <tt>execute</tt> method invokes the - * {@link RejectedExecutionHandler#rejectedExecution} method of its - * {@link RejectedExecutionHandler}. Four predefined handler policies - * are provided: + * <dd> New tasks submitted in method {@link #execute} will be + * <em>rejected</em> when the Executor has been shut down, and also + * when the Executor uses finite bounds for both maximum threads and + * work queue capacity, and is saturated. In either case, the {@code + * execute} method invokes the {@link + * RejectedExecutionHandler#rejectedExecution} method of its {@link + * RejectedExecutionHandler}. Four predefined handler policies are + * provided: * * <ol> * - * <li> In the - * default {@link ThreadPoolExecutor.AbortPolicy}, the handler throws a - * runtime {@link RejectedExecutionException} upon rejection. </li> - * - * <li> In {@link - * ThreadPoolExecutor.CallerRunsPolicy}, the thread that invokes - * <tt>execute</tt> itself runs the task. This provides a simple - * feedback control mechanism that will slow down the rate that new - * tasks are submitted. </li> + * <li> In the default {@link ThreadPoolExecutor.AbortPolicy}, the + * handler throws a runtime {@link RejectedExecutionException} upon + * rejection. </li> * - * <li> In {@link ThreadPoolExecutor.DiscardPolicy}, - * a task that cannot be executed is simply dropped. </li> + * <li> In {@link ThreadPoolExecutor.CallerRunsPolicy}, the thread + * that invokes {@code execute} itself runs the task. This provides a + * simple feedback control mechanism that will slow down the rate that + * new tasks are submitted. </li> * - * <li>In {@link - * ThreadPoolExecutor.DiscardOldestPolicy}, if the executor is not - * shut down, the task at the head of the work queue is dropped, and - * then execution is retried (which can fail again, causing this to be - * repeated.) </li> + * <li> In {@link ThreadPoolExecutor.DiscardPolicy}, a task that + * cannot be executed is simply dropped. </li> + * + * <li>In {@link ThreadPoolExecutor.DiscardOldestPolicy}, if the + * executor is not shut down, the task at the head of the work queue + * is dropped, and then execution is retried (which can fail again, + * causing this to be repeated.) </li> * * </ol> * @@ -204,50 +200,62 @@ import java.util.*; * * <dt>Hook methods</dt> * - * <dd>This class provides <tt>protected</tt> overridable {@link - * ThreadPoolExecutor#beforeExecute} and {@link - * ThreadPoolExecutor#afterExecute} methods that are called before and - * after execution of each task. These can be used to manipulate the - * execution environment, for example, reinitializing ThreadLocals, - * gathering statistics, or adding log entries. Additionally, method - * {@link ThreadPoolExecutor#terminated} can be overridden to perform - * any special processing that needs to be done once the Executor has - * fully terminated.</dd> + * <dd>This class provides {@code protected} overridable {@link + * #beforeExecute} and {@link #afterExecute} methods that are called + * before and after execution of each task. These can be used to + * manipulate the execution environment; for example, reinitializing + * ThreadLocals, gathering statistics, or adding log + * entries. Additionally, method {@link #terminated} can be overridden + * to perform any special processing that needs to be done once the + * Executor has fully terminated. + * + * <p>If hook or callback methods throw exceptions, internal worker + * threads may in turn fail and abruptly terminate.</dd> * * <dt>Queue maintenance</dt> * - * <dd> Method {@link ThreadPoolExecutor#getQueue} allows access to - * the work queue for purposes of monitoring and debugging. Use of - * this method for any other purpose is strongly discouraged. Two - * supplied methods, {@link ThreadPoolExecutor#remove} and {@link - * ThreadPoolExecutor#purge} are available to assist in storage - * reclamation when large numbers of queued tasks become - * cancelled.</dd> </dl> + * <dd> Method {@link #getQueue} allows access to the work queue for + * purposes of monitoring and debugging. Use of this method for any + * other purpose is strongly discouraged. Two supplied methods, + * {@link #remove} and {@link #purge} are available to assist in + * storage reclamation when large numbers of queued tasks become + * cancelled.</dd> + * + * <dt>Finalization</dt> + * + * <dd> A pool that is no longer referenced in a program <em>AND</em> + * has no remaining threads will be {@code shutdown} automatically. If + * you would like to ensure that unreferenced pools are reclaimed even + * if users forget to call {@link #shutdown}, then you must arrange + * that unused threads eventually die, by setting appropriate + * keep-alive times using a lower bound of zero core threads. </dd> + * + * </dl> * * <p> <b>Extension example</b>. Most extensions of this class * override one or more of the protected hook methods. For example, * here is a subclass that adds a simple pause/resume feature: * - * <pre> + * <pre> {@code * class PausableThreadPoolExecutor extends ThreadPoolExecutor { * private boolean isPaused; * private ReentrantLock pauseLock = new ReentrantLock(); * private Condition unpaused = pauseLock.newCondition(); * * public PausableThreadPoolExecutor(...) { super(...); } - * + * * protected void beforeExecute(Thread t, Runnable r) { * super.beforeExecute(t, r); * pauseLock.lock(); * try { * while (isPaused) unpaused.await(); - * } catch(InterruptedException ie) { + * } catch (InterruptedException ie) { * t.interrupt(); * } finally { * pauseLock.unlock(); * } * } - * + * * public void pause() { * pauseLock.lock(); * try { @@ -256,7 +264,7 @@ import java.util.*; * pauseLock.unlock(); * } * } - * + * * public void resume() { * pauseLock.lock(); * try { @@ -266,86 +274,196 @@ import java.util.*; * pauseLock.unlock(); * } * } - * } - * </pre> + * }}</pre> + * * @since 1.5 * @author Doug Lea */ public class ThreadPoolExecutor extends AbstractExecutorService { /** - * Only used to force toArray() to produce a Runnable[]. + * The main pool control state, ctl, is an atomic integer packing + * two conceptual fields + * workerCount, indicating the effective number of threads + * runState, indicating whether running, shutting down etc + * + * In order to pack them into one int, we limit workerCount to + * (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2 + * billion) otherwise representable. If this is ever an issue in + * the future, the variable can be changed to be an AtomicLong, + * and the shift/mask constants below adjusted. But until the need + * arises, this code is a bit faster and simpler using an int. + * + * The workerCount is the number of workers that have been + * permitted to start and not permitted to stop. The value may be + * transiently different from the actual number of live threads, + * for example when a ThreadFactory fails to create a thread when + * asked, and when exiting threads are still performing + * bookkeeping before terminating. The user-visible pool size is + * reported as the current size of the workers set. + * + * The runState provides the main lifecyle control, taking on values: + * + * RUNNING: Accept new tasks and process queued tasks + * SHUTDOWN: Don't accept new tasks, but process queued tasks + * STOP: Don't accept new tasks, don't process queued tasks, + * and interrupt in-progress tasks + * TIDYING: All tasks have terminated, workerCount is zero, + * the thread transitioning to state TIDYING + * will run the terminated() hook method + * TERMINATED: terminated() has completed + * + * The numerical order among these values matters, to allow + * ordered comparisons. The runState monotonically increases over + * time, but need not hit each state. The transitions are: + * + * RUNNING -> SHUTDOWN + * On invocation of shutdown(), perhaps implicitly in finalize() + * (RUNNING or SHUTDOWN) -> STOP + * On invocation of shutdownNow() + * SHUTDOWN -> TIDYING + * When both queue and pool are empty + * STOP -> TIDYING + * When pool is empty + * TIDYING -> TERMINATED + * When the terminated() hook method has completed + * + * Threads waiting in awaitTermination() will return when the + * state reaches TERMINATED. + * + * Detecting the transition from SHUTDOWN to TIDYING is less + * straightforward than you'd like because the queue may become + * empty after non-empty and vice versa during SHUTDOWN state, but + * we can only terminate if, after seeing that it is empty, we see + * that workerCount is 0 (which sometimes entails a recheck -- see + * below). */ - private static final Runnable[] EMPTY_RUNNABLE_ARRAY = new Runnable[0]; + private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); + private static final int COUNT_BITS = Integer.SIZE - 3; + private static final int CAPACITY = (1 << COUNT_BITS) - 1; + + // runState is stored in the high-order bits + private static final int RUNNING = -1 << COUNT_BITS; + private static final int SHUTDOWN = 0 << COUNT_BITS; + private static final int STOP = 1 << COUNT_BITS; + private static final int TIDYING = 2 << COUNT_BITS; + private static final int TERMINATED = 3 << COUNT_BITS; + + // Packing and unpacking ctl + private static int runStateOf(int c) { return c & ~CAPACITY; } + private static int workerCountOf(int c) { return c & CAPACITY; } + private static int ctlOf(int rs, int wc) { return rs | wc; } + + /* + * Bit field accessors that don't require unpacking ctl. + * These depend on the bit layout and on workerCount being never negative. + */ + + private static boolean runStateLessThan(int c, int s) { + return c < s; + } + + private static boolean runStateAtLeast(int c, int s) { + return c >= s; + } + + private static boolean isRunning(int c) { + return c < SHUTDOWN; + } /** - * Permission for checking shutdown + * Attempt to CAS-increment the workerCount field of ctl. */ - private static final RuntimePermission shutdownPerm = - new RuntimePermission("modifyThread"); + private boolean compareAndIncrementWorkerCount(int expect) { + return ctl.compareAndSet(expect, expect + 1); + } /** - * Queue used for holding tasks and handing off to worker threads. + * Attempt to CAS-decrement the workerCount field of ctl. */ - private final BlockingQueue<Runnable> workQueue; + private boolean compareAndDecrementWorkerCount(int expect) { + return ctl.compareAndSet(expect, expect - 1); + } /** - * Lock held on updates to poolSize, corePoolSize, maximumPoolSize, and - * workers set. + * Decrements the workerCount field of ctl. This is called only on + * abrupt termination of a thread (see processWorkerExit). Other + * decrements are performed within getTask. */ - private final ReentrantLock mainLock = new ReentrantLock(); + private void decrementWorkerCount() { + do {} while (! compareAndDecrementWorkerCount(ctl.get())); + } /** - * Wait condition to support awaitTermination + * The queue used for holding tasks and handing off to worker + * threads. We do not require that workQueue.poll() returning + * null necessarily means that workQueue.isEmpty(), so rely + * solely on isEmpty to see if the queue is empty (which we must + * do for example when deciding whether to transition from + * SHUTDOWN to TIDYING). This accommodates special-purpose + * queues such as DelayQueues for which poll() is allowed to + * return null even if it may later return non-null when delays + * expire. */ - private final Condition termination = mainLock.newCondition(); + private final BlockingQueue<Runnable> workQueue; /** - * Set containing all worker threads in pool. + * Lock held on access to workers set and related bookkeeping. + * While we could use a concurrent set of some sort, it turns out + * to be generally preferable to use a lock. Among the reasons is + * that this serializes interruptIdleWorkers, which avoids + * unnecessary interrupt storms, especially during shutdown. + * Otherwise exiting threads would concurrently interrupt those + * that have not yet interrupted. It also simplifies some of the + * associated statistics bookkeeping of largestPoolSize etc. We + * also hold mainLock on shutdown and shutdownNow, for the sake of + * ensuring workers set is stable while separately checking + * permission to interrupt and actually interrupting. */ - private final HashSet<Worker> workers = new HashSet<Worker>(); + private final ReentrantLock mainLock = new ReentrantLock(); /** - * Timeout in nanoseconds for idle threads waiting for work. - * Threads use this timeout only when there are more than - * corePoolSize present. Otherwise they wait forever for new work. + * Set containing all worker threads in pool. Accessed only when + * holding mainLock. */ - private volatile long keepAliveTime; + private final HashSet<Worker> workers = new HashSet<Worker>(); /** - * Core pool size, updated only while holding mainLock, - * but volatile to allow concurrent readability even - * during updates. + * Wait condition to support awaitTermination */ - private volatile int corePoolSize; + private final Condition termination = mainLock.newCondition(); /** - * Maximum pool size, updated only while holding mainLock - * but volatile to allow concurrent readability even - * during updates. + * Tracks largest attained pool size. Accessed only under + * mainLock. */ - private volatile int maximumPoolSize; + private int largestPoolSize; /** - * Current pool size, updated only while holding mainLock - * but volatile to allow concurrent readability even - * during updates. + * Counter for completed tasks. Updated only on termination of + * worker threads. Accessed only under mainLock. */ - private volatile int poolSize; + private long completedTaskCount; - /** - * Lifecycle state + /* + * All user control parameters are declared as volatiles so that + * ongoing actions are based on freshest values, but without need + * for locking, since no internal invariants depend on them + * changing synchronously with respect to other actions. */ - volatile int runState; - // Special values for runState - /** Normal, not-shutdown mode */ - static final int RUNNING = 0; - /** Controlled shutdown mode */ - static final int SHUTDOWN = 1; - /** Immediate shutdown mode */ - static final int STOP = 2; - /** Final state */ - static final int TERMINATED = 3; + /** + * Factory for new threads. All threads are created using this + * factory (via method addWorker). All callers must be prepared + * for addWorker to fail, which may reflect a system or user's + * policy limiting the number of threads. Even though it is not + * treated as an error, failure to create threads may result in + * new tasks being rejected or existing ones remaining stuck in + * the queue. On the other hand, no special precautions exist to + * handle OutOfMemoryErrors that might be thrown while trying to + * create threads, since there is generally no recourse from + * within this class. + */ + private volatile ThreadFactory threadFactory; /** * Handler called when saturated or shutdown in execute. @@ -353,21 +471,24 @@ public class ThreadPoolExecutor extends AbstractExecutorService { private volatile RejectedExecutionHandler handler; /** - * Factory for new threads. + * Timeout in nanoseconds for idle threads waiting for work. + * Threads use this timeout when there are more than corePoolSize + * present. Otherwise they wait forever for new work. */ - private volatile ThreadFactory threadFactory; + private volatile long keepAliveTime; /** - * Tracks largest attained pool size. + * Core pool size is the minimum number of workers to keep alive + * (and not allow to time out etc). */ - private int largestPoolSize; + private volatile int corePoolSize; /** - * Counter for completed tasks. Updated only on termination of - * worker threads. + * Maximum pool size. Note that the actual maximum is internally + * bounded by CAPACITY. */ - private long completedTaskCount; - + private volatile int maximumPoolSize; + /** * The default rejected execution handler */ @@ -375,336 +496,622 @@ public class ThreadPoolExecutor extends AbstractExecutorService { new AbortPolicy(); /** - * Invoke the rejected execution handler for the given command. + * Permission required for callers of shutdown and shutdownNow. + * We additionally require (see checkShutdownAccess) that callers + * have permission to actually interrupt threads in the worker set + * (as governed by Thread.interrupt, which relies on + * ThreadGroup.checkAccess, which in turn relies on + * SecurityManager.checkAccess). Shutdowns are attempted only if + * these checks pass. + * + * All actual invocations of Thread.interrupt (see + * interruptIdleWorkers and interruptWorkers) ignore + * SecurityExceptions, meaning that the attempted interrupts + * silently fail. In the case of shutdown, they should not fail + * unless the SecurityManager has inconsistent policies, sometimes + * allowing access to a thread and sometimes not. In such cases, + * failure to actually interrupt threads may disable or delay full + * termination. Other uses of interruptIdleWorkers are advisory, + * and failure to actually interrupt will merely delay response to + * configuration changes so is not handled exceptionally. */ - void reject(Runnable command) { - handler.rejectedExecution(command, this); + private static final RuntimePermission shutdownPerm = + new RuntimePermission("modifyThread"); + + /** + * Class Worker mainly maintains interrupt control state for + * threads running tasks, along with other minor bookkeeping. + * This class opportunistically extends AbstractQueuedSynchronizer + * to simplify acquiring and releasing a lock surrounding each + * task execution. This protects against interrupts that are + * intended to wake up a worker thread waiting for a task from + * instead interrupting a task being run. We implement a simple + * non-reentrant mutual exclusion lock rather than use ReentrantLock + * because we do not want worker tasks to be able to reacquire the + * lock when they invoke pool control methods like setCorePoolSize. + */ + private final class Worker + extends AbstractQueuedSynchronizer + implements Runnable + { + /** + * This class will never be serialized, but we provide a + * serialVersionUID to suppress a javac warning. + */ + private static final long serialVersionUID = 6138294804551838833L; + + /** Thread this worker is running in. Null if factory fails. */ + final Thread thread; + /** Initial task to run. Possibly null. */ + Runnable firstTask; + /** Per-thread task counter */ + volatile long completedTasks; + + /** + * Creates with given first task and thread from ThreadFactory. + * @param firstTask the first task (null if none) + */ + Worker(Runnable firstTask) { + this.firstTask = firstTask; + this.thread = getThreadFactory().newThread(this); + } + + /** Delegates main run loop to outer runWorker */ + public void run() { + runWorker(this); + } + + // Lock methods + // + // The value 0 represents the unlocked state. + // The value 1 represents the locked state. + + protected boolean isHeldExclusively() { + return getState() == 1; + } + + protected boolean tryAcquire(int unused) { + if (compareAndSetState(0, 1)) { + setExclusiveOwnerThread(Thread.currentThread()); + return true; + } + return false; + } + + protected boolean tryRelease(int unused) { + setExclusiveOwnerThread(null); + setState(0); + return true; + } + + public void lock() { acquire(1); } + public boolean tryLock() { return tryAcquire(1); } + public void unlock() { release(1); } + public boolean isLocked() { return isHeldExclusively(); } } + /* + * Methods for setting control state + */ + /** - * Create and return a new thread running firstTask as its first - * task. Call only while holding mainLock - * @param firstTask the task the new thread should run first (or - * null if none) - * @return the new thread + * Transitions runState to given target, or leaves it alone if + * already at least the given target. + * + * @param targetState the desired state, either SHUTDOWN or STOP + * (but not TIDYING or TERMINATED -- use tryTerminate for that) */ - private Thread addThread(Runnable firstTask) { - Worker w = new Worker(firstTask); - Thread t = threadFactory.newThread(w); - w.thread = t; - workers.add(w); - int nt = ++poolSize; - if (nt > largestPoolSize) - largestPoolSize = nt; - return t; + private void advanceRunState(int targetState) { + for (;;) { + int c = ctl.get(); + if (runStateAtLeast(c, targetState) || + ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c)))) + break; + } } /** - * Create and start a new thread running firstTask as its first - * task, only if fewer than corePoolSize threads are running. - * @param firstTask the task the new thread should run first (or - * null if none) - * @return true if successful. + * Transitions to TERMINATED state if either (SHUTDOWN and pool + * and queue empty) or (STOP and pool empty). If otherwise + * eligible to terminate but workerCount is nonzero, interrupts an + * idle worker to ensure that shutdown signals propagate. This + * method must be called following any action that might make + * termination possible -- reducing worker count or removing tasks + * from the queue during shutdown. The method is non-private to + * allow access from ScheduledThreadPoolExecutor. + */ + final void tryTerminate() { + for (;;) { + int c = ctl.get(); + if (isRunning(c) || + runStateAtLeast(c, TIDYING) || + (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty())) + return; + if (workerCountOf(c) != 0) { // Eligible to terminate + interruptIdleWorkers(ONLY_ONE); + return; + } + + final ReentrantLock mainLock = this.mainLock; + mainLock.lock(); + try { + if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { + try { + terminated(); + } finally { + ctl.set(ctlOf(TERMINATED, 0)); + termination.signalAll(); + } + return; + } + } finally { + mainLock.unlock(); + } + // else retry on failed CAS + } + } + + /* + * Methods for controlling interrupts to worker threads. */ - private boolean addIfUnderCorePoolSize(Runnable firstTask) { - Thread t = null; + + /** + * If there is a security manager, makes sure caller has + * permission to shut down threads in general (see shutdownPerm). + * If this passes, additionally makes sure the caller is allowed + * to interrupt each worker thread. This might not be true even if + * first check passed, if the SecurityManager treats some threads + * specially. + */ + private void checkShutdownAccess() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkPermission(shutdownPerm); + final ReentrantLock mainLock = this.mainLock; + mainLock.lock(); + try { + for (Worker w : workers) + security.checkAccess(w.thread); + } finally { + mainLock.unlock(); + } + } + } + + /** + * Interrupts all threads, even if active. Ignores SecurityExceptions + * (in which case some threads may remain uninterrupted). + */ + private void interruptWorkers() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { - if (poolSize < corePoolSize) - t = addThread(firstTask); + for (Worker w : workers) { + try { + w.thread.interrupt(); + } catch (SecurityException ignore) { + } + } } finally { mainLock.unlock(); } - if (t == null) - return false; - t.start(); - return true; } /** - * Create and start a new thread only if fewer than maximumPoolSize - * threads are running. The new thread runs as its first task the - * next task in queue, or if there is none, the given task. - * @param firstTask the task the new thread should run first (or - * null if none) - * @return null on failure, else the first task to be run by new thread. + * Interrupts threads that might be waiting for tasks (as + * indicated by not being locked) so they can check for + * termination or configuration changes. Ignores + * SecurityExceptions (in which case some threads may remain + * uninterrupted). + * + * @param onlyOne If true, interrupt at most one worker. This is + * called only from tryTerminate when termination is otherwise + * enabled but there are still other workers. In this case, at + * most one waiting worker is interrupted to propagate shutdown + * signals in case all threads are currently waiting. + * Interrupting any arbitrary thread ensures that newly arriving + * workers since shutdown began will also eventually exit. + * To guarantee eventual termination, it suffices to always + * interrupt only one idle worker, but shutdown() interrupts all + * idle workers so that redundant workers exit promptly, not + * waiting for a straggler task to finish. */ - private Runnable addIfUnderMaximumPoolSize(Runnable firstTask) { - Thread t = null; - Runnable next = null; + private void interruptIdleWorkers(boolean onlyOne) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { - if (poolSize < maximumPoolSize) { - next = workQueue.poll(); - if (next == null) - next = firstTask; - t = addThread(next); + for (Worker w : workers) { + Thread t = w.thread; + if (!t.isInterrupted() && w.tryLock()) { + try { + t.interrupt(); + } catch (SecurityException ignore) { + } finally { + w.unlock(); + } + } + if (onlyOne) + break; } } finally { mainLock.unlock(); } - if (t == null) - return null; - t.start(); - return next; } + /** + * Common form of interruptIdleWorkers, to avoid having to + * remember what the boolean argument means. + */ + private void interruptIdleWorkers() { + interruptIdleWorkers(false); + } + + private static final boolean ONLY_ONE = true; /** - * Get the next task for a worker thread to run. - * @return the task - * @throws InterruptedException if interrupted while waiting for task + * Ensures that unless the pool is stopping, the current thread + * does not have its interrupt set. This requires a double-check + * of state in case the interrupt was cleared concurrently with a + * shutdownNow -- if so, the interrupt is re-enabled. */ - Runnable getTask() throws InterruptedException { - for (;;) { - switch(runState) { - case RUNNING: { - if (poolSize <= corePoolSize) // untimed wait if core - return workQueue.take(); - - long timeout = keepAliveTime; - if (timeout <= 0) // die immediately for 0 timeout - return null; - Runnable r = workQueue.poll(timeout, TimeUnit.NANOSECONDS); - if (r != null) - return r; - if (poolSize > corePoolSize) // timed out - return null; - // else, after timeout, pool shrank so shouldn't die, so retry - break; - } + private void clearInterruptsForTaskRun() { + if (runStateLessThan(ctl.get(), STOP) && + Thread.interrupted() && + runStateAtLeast(ctl.get(), STOP)) + Thread.currentThread().interrupt(); + } - case SHUTDOWN: { - // Help drain queue - Runnable r = workQueue.poll(); - if (r != null) - return r; - - // Check if can terminate - if (workQueue.isEmpty()) { - interruptIdleWorkers(); - return null; - } + /* + * Misc utilities, most of which are also exported to + * ScheduledThreadPoolExecutor + */ - // There could still be delayed tasks in queue. - // Wait for one, re-checking state upon interruption - try { - return workQueue.take(); - } catch(InterruptedException ignore) {} - break; - } + /** + * Invokes the rejected execution handler for the given command. + * Package-protected for use by ScheduledThreadPoolExecutor. + */ + final void reject(Runnable command) { + handler.rejectedExecution(command, this); + } - case STOP: - return null; - default: - assert false; + /** + * Performs any further cleanup following run state transition on + * invocation of shutdown. A no-op here, but used by + * ScheduledThreadPoolExecutor to cancel delayed tasks. + */ + void onShutdown() { + } + + /** + * State check needed by ScheduledThreadPoolExecutor to + * enable running tasks during shutdown. + * + * @param shutdownOK true if should return true if SHUTDOWN + */ + final boolean isRunningOrShutdown(boolean shutdownOK) { + int rs = runStateOf(ctl.get()); + return rs == RUNNING || (rs == SHUTDOWN && shutdownOK); + } + + /** + * Drains the task queue into a new list, normally using + * drainTo. But if the queue is a DelayQueue or any other kind of + * queue for which poll or drainTo may fail to remove some + * elements, it deletes them one by one. + */ + private List<Runnable> drainQueue() { + BlockingQueue<Runnable> q = workQueue; + List<Runnable> taskList = new ArrayList<Runnable>(); + q.drainTo(taskList); + if (!q.isEmpty()) { + for (Runnable r : q.toArray(new Runnable[0])) { + if (q.remove(r)) + taskList.add(r); } } + return taskList; } + /* + * Methods for creating, running and cleaning up after workers + */ + /** - * Wake up all threads that might be waiting for tasks. + * Checks if a new worker can be added with respect to current + * pool state and the given bound (either core or maximum). If so, + * the worker count is adjusted accordingly, and, if possible, a + * new worker is created and started running firstTask as its + * first task. This method returns false if the pool is stopped or + * eligible to shut down. It also returns false if the thread + * factory fails to create a thread when asked, which requires a + * backout of workerCount, and a recheck for termination, in case + * the existence of this worker was holding up termination. + * + * @param firstTask the task the new thread should run first (or + * null if none). Workers are created with an initial first task + * (in method execute()) to bypass queuing when there are fewer + * than corePoolSize threads (in which case we always start one), + * or when the queue is full (in which case we must bypass queue). + * Initially idle threads are usually created via + * prestartCoreThread or to replace other dying workers. + * + * @param core if true use corePoolSize as bound, else + * maximumPoolSize. (A boolean indicator is used here rather than a + * value to ensure reads of fresh values after checking other pool + * state). + * @return true if successful */ - void interruptIdleWorkers() { + private boolean addWorker(Runnable firstTask, boolean core) { + retry: + for (;;) { + int c = ctl.get(); + int rs = runStateOf(c); + + // Check if queue empty only if necessary. + if (rs >= SHUTDOWN && + ! (rs == SHUTDOWN && + firstTask == null && + ! workQueue.isEmpty())) + return false; + + for (;;) { + int wc = workerCountOf(c); + if (wc >= CAPACITY || + wc >= (core ? corePoolSize : maximumPoolSize)) + return false; + if (compareAndIncrementWorkerCount(c)) + break retry; + c = ctl.get(); // Re-read ctl + if (runStateOf(c) != rs) + continue retry; + // else CAS failed due to workerCount change; retry inner loop + } + } + + Worker w = new Worker(firstTask); + Thread t = w.thread; + final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { - for (Worker w : workers) - w.interruptIfIdle(); + // Recheck while holding lock. + // Back out on ThreadFactory failure or if + // shut down before lock acquired. + int c = ctl.get(); + int rs = runStateOf(c); + + if (t == null || + (rs >= SHUTDOWN && + ! (rs == SHUTDOWN && + firstTask == null))) { + decrementWorkerCount(); + tryTerminate(); + return false; + } + + workers.add(w); + + int s = workers.size(); + if (s > largestPoolSize) + largestPoolSize = s; } finally { mainLock.unlock(); } + + t.start(); + // It is possible (but unlikely) for a thread to have been + // added to workers, but not yet started, during transition to + // STOP, which could result in a rare missed interrupt, + // because Thread.interrupt is not guaranteed to have any effect + // on a non-yet-started Thread (see Thread#interrupt). + if (runStateOf(ctl.get()) == STOP && ! t.isInterrupted()) + t.interrupt(); + + return true; } /** - * Perform bookkeeping for a terminated worker thread. + * Performs cleanup and bookkeeping for a dying worker. Called + * only from worker threads. Unless completedAbruptly is set, + * assumes that workerCount has already been adjusted to account + * for exit. This method removes thread from worker set, and + * possibly terminates the pool or replaces the worker if either + * it exited due to user task exception or if fewer than + * corePoolSize workers are running or queue is non-empty but + * there are no workers. + * * @param w the worker + * @param completedAbruptly if the worker died due to user exception */ - void workerDone(Worker w) { + private void processWorkerExit(Worker w, boolean completedAbruptly) { + if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted + decrementWorkerCount(); + final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { completedTaskCount += w.completedTasks; workers.remove(w); - if (--poolSize > 0) - return; - - // Else, this is the last thread. Deal with potential shutdown. - - int state = runState; - assert state != TERMINATED; - - if (state != STOP) { - // If there are queued tasks but no threads, create - // replacement. - Runnable r = workQueue.poll(); - if (r != null) { - addThread(r).start(); - return; - } - - // If there are some (presumably delayed) tasks but - // none pollable, create an idle replacement to wait. - if (!workQueue.isEmpty()) { - addThread(null).start(); - return; - } - - // Otherwise, we can exit without replacement - if (state == RUNNING) - return; - } - - // Either state is STOP, or state is SHUTDOWN and there is - // no work to do. So we can terminate. - termination.signalAll(); - runState = TERMINATED; - // fall through to call terminate() outside of lock. } finally { mainLock.unlock(); } - assert runState == TERMINATED; - terminated(); + tryTerminate(); + + int c = ctl.get(); + if (runStateLessThan(c, STOP)) { + if (!completedAbruptly) { + int min = corePoolSize; + if (min == 0 && ! workQueue.isEmpty()) + min = 1; + if (workerCountOf(c) >= min) + return; // replacement not needed + } + addWorker(null, false); + } } /** - * Worker threads + * Performs blocking or timed wait for a task, depending on + * current configuration settings, or returns null if this worker + * must exit because of any of: + * 1. There are more than maximumPoolSize workers (due to + * a call to setMaximumPoolSize). + * 2. The pool is stopped. + * 3. The pool is shutdown and the queue is empty. + * 4. This worker timed out waiting for a task, and timed-out + * workers are subject to termination (that is, + * {@code workerCount > corePoolSize}) + * both before and after the timed wait. + * + * @return task, or null if the worker must exit, in which case + * workerCount is decremented */ - private class Worker implements Runnable { + private Runnable getTask() { + boolean timedOut = false; // Did the last poll() time out? - /** - * The runLock is acquired and released surrounding each task - * execution. It mainly protects against interrupts that are - * intended to cancel the worker thread from instead - * interrupting the task being run. - */ - private final ReentrantLock runLock = new ReentrantLock(); - - /** - * Initial task to run before entering run loop - */ - private Runnable firstTask; - - /** - * Per thread completed task counter; accumulated - * into completedTaskCount upon termination. - */ - volatile long completedTasks; + retry: + for (;;) { + int c = ctl.get(); + int rs = runStateOf(c); - /** - * Thread this worker is running in. Acts as a final field, - * but cannot be set until thread is created. - */ - Thread thread; + // Check if queue empty only if necessary. + if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { + decrementWorkerCount(); + return null; + } - Worker(Runnable firstTask) { - this.firstTask = firstTask; - } + boolean timed; // Are workers subject to culling? - boolean isActive() { - return runLock.isLocked(); - } + for (;;) { + int wc = workerCountOf(c); + timed = wc > corePoolSize; - /** - * Interrupt thread if not running a task - */ - void interruptIfIdle() { - final ReentrantLock runLock = this.runLock; - if (runLock.tryLock()) { - try { - thread.interrupt(); - } finally { - runLock.unlock(); - } + if (wc <= maximumPoolSize && ! (timedOut && timed)) + break; + if (compareAndDecrementWorkerCount(c)) + return null; + c = ctl.get(); // Re-read ctl + if (runStateOf(c) != rs) + continue retry; + // else CAS failed due to workerCount change; retry inner loop } - } - - /** - * Cause thread to die even if running a task. - */ - void interruptNow() { - thread.interrupt(); - } - /** - * Run a single task between before/after methods. - */ - private void runTask(Runnable task) { - final ReentrantLock runLock = this.runLock; - runLock.lock(); try { - // Abort now if immediate cancel. Otherwise, we have - // committed to run this task. - if (runState == STOP) - return; - - Thread.interrupted(); // clear interrupt status on entry - boolean ran = false; - beforeExecute(thread, task); - try { - task.run(); - ran = true; - afterExecute(task, null); - ++completedTasks; - } catch(RuntimeException ex) { - if (!ran) - afterExecute(task, ex); - // Else the exception occurred within - // afterExecute itself in which case we don't - // want to call it again. - throw ex; - } - } finally { - runLock.unlock(); + Runnable r = timed ? + workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : + workQueue.take(); + if (r != null) + return r; + timedOut = true; + } catch (InterruptedException retry) { + timedOut = false; } } + } - /** - * Main run loop - */ - public void run() { - try { - Runnable task = firstTask; - firstTask = null; - while (task != null || (task = getTask()) != null) { - runTask(task); - task = null; // unnecessary but can help GC + /** + * Main worker run loop. Repeatedly gets tasks from queue and + * executes them, while coping with a number of issues: + * + * 1. We may start out with an initial task, in which case we + * don't need to get the first one. Otherwise, as long as pool is + * running, we get tasks from getTask. If it returns null then the + * worker exits due to changed pool state or configuration + * parameters. Other exits result from exception throws in + * external code, in which case completedAbruptly holds, which + * usually leads processWorkerExit to replace this thread. + * + * 2. Before running any task, the lock is acquired to prevent + * other pool interrupts while the task is executing, and + * clearInterruptsForTaskRun called to ensure that unless pool is + * stopping, this thread does not have its interrupt set. + * + * 3. Each task run is preceded by a call to beforeExecute, which + * might throw an exception, in which case we cause thread to die + * (breaking loop with completedAbruptly true) without processing + * the task. + * + * 4. Assuming beforeExecute completes normally, we run the task, + * gathering any of its thrown exceptions to send to + * afterExecute. We separately handle RuntimeException, Error + * (both of which the specs guarantee that we trap) and arbitrary + * Throwables. Because we cannot rethrow Throwables within + * Runnable.run, we wrap them within Errors on the way out (to the + * thread's UncaughtExceptionHandler). Any thrown exception also + * conservatively causes thread to die. + * + * 5. After task.run completes, we call afterExecute, which may + * also throw an exception, which will also cause thread to + * die. According to JLS Sec 14.20, this exception is the one that + * will be in effect even if task.run throws. + * + * The net effect of the exception mechanics is that afterExecute + * and the thread's UncaughtExceptionHandler have as accurate + * information as we can provide about any problems encountered by + * user code. + * + * @param w the worker + */ + final void runWorker(Worker w) { + Runnable task = w.firstTask; + w.firstTask = null; + boolean completedAbruptly = true; + try { + while (task != null || (task = getTask()) != null) { + w.lock(); + clearInterruptsForTaskRun(); + try { + beforeExecute(w.thread, task); + Throwable thrown = null; + try { + task.run(); + } catch (RuntimeException x) { + thrown = x; throw x; + } catch (Error x) { + thrown = x; throw x; + } catch (Throwable x) { + thrown = x; throw new Error(x); + } finally { + afterExecute(task, thrown); + } + } finally { + task = null; + w.completedTasks++; + w.unlock(); } - } catch(InterruptedException ie) { - // fall through - } finally { - workerDone(this); } + completedAbruptly = false; + } finally { + processWorkerExit(w, completedAbruptly); } } - // Public methods + // Public constructors and methods /** - * Creates a new <tt>ThreadPoolExecutor</tt> with the given - * initial parameters and default thread factory and handler. It - * may be more convenient to use one of the {@link Executors} - * factory methods instead of this general purpose constructor. + * Creates a new {@code ThreadPoolExecutor} with the given initial + * parameters and default thread factory and rejected execution handler. + * It may be more convenient to use one of the {@link Executors} factory + * methods instead of this general purpose constructor. * - * @param corePoolSize the number of threads to keep in the - * pool, even if they are idle. + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle * @param maximumPoolSize the maximum number of threads to allow in the - * pool. + * pool * @param keepAliveTime when the number of threads is greater than - * the core, this is the maximum time that excess idle threads - * will wait for new tasks before terminating. - * @param unit the time unit for the keepAliveTime - * argument. - * @param workQueue the queue to use for holding tasks before they - * are executed. This queue will hold only the <tt>Runnable</tt> - * tasks submitted by the <tt>execute</tt> method. - * @throws IllegalArgumentException if corePoolSize, or - * keepAliveTime less than zero, or if maximumPoolSize less than or - * equal to zero, or if corePoolSize greater than maximumPoolSize. - * @throws NullPointerException if <tt>workQueue</tt> is null + * the core, this is the maximum time that excess idle threads + * will wait for new tasks before terminating. + * @param unit the time unit for the {@code keepAliveTime} argument + * @param workQueue the queue to use for holding tasks before they are + * executed. This queue will hold only the {@code Runnable} + * tasks submitted by the {@code execute} method. + * @throws IllegalArgumentException if one of the following holds:<br> + * {@code corePoolSize < 0}<br> + * {@code keepAliveTime < 0}<br> + * {@code maximumPoolSize <= 0}<br> + * {@code maximumPoolSize < corePoolSize} + * @throws NullPointerException if {@code workQueue} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, @@ -716,28 +1123,29 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } /** - * Creates a new <tt>ThreadPoolExecutor</tt> with the given initial - * parameters. + * Creates a new {@code ThreadPoolExecutor} with the given initial + * parameters and default rejected execution handler. * - * @param corePoolSize the number of threads to keep in the - * pool, even if they are idle. + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle * @param maximumPoolSize the maximum number of threads to allow in the - * pool. + * pool * @param keepAliveTime when the number of threads is greater than - * the core, this is the maximum time that excess idle threads - * will wait for new tasks before terminating. - * @param unit the time unit for the keepAliveTime - * argument. - * @param workQueue the queue to use for holding tasks before they - * are executed. This queue will hold only the <tt>Runnable</tt> - * tasks submitted by the <tt>execute</tt> method. + * the core, this is the maximum time that excess idle threads + * will wait for new tasks before terminating. + * @param unit the time unit for the {@code keepAliveTime} argument + * @param workQueue the queue to use for holding tasks before they are + * executed. This queue will hold only the {@code Runnable} + * tasks submitted by the {@code execute} method. * @param threadFactory the factory to use when the executor - * creates a new thread. - * @throws IllegalArgumentException if corePoolSize, or - * keepAliveTime less than zero, or if maximumPoolSize less than or - * equal to zero, or if corePoolSize greater than maximumPoolSize. - * @throws NullPointerException if <tt>workQueue</tt> - * or <tt>threadFactory</tt> are null. + * creates a new thread + * @throws IllegalArgumentException if one of the following holds:<br> + * {@code corePoolSize < 0}<br> + * {@code keepAliveTime < 0}<br> + * {@code maximumPoolSize <= 0}<br> + * {@code maximumPoolSize < corePoolSize} + * @throws NullPointerException if {@code workQueue} + * or {@code threadFactory} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, @@ -750,28 +1158,29 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } /** - * Creates a new <tt>ThreadPoolExecutor</tt> with the given initial - * parameters. + * Creates a new {@code ThreadPoolExecutor} with the given initial + * parameters and default thread factory. * - * @param corePoolSize the number of threads to keep in the - * pool, even if they are idle. + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle * @param maximumPoolSize the maximum number of threads to allow in the - * pool. + * pool * @param keepAliveTime when the number of threads is greater than - * the core, this is the maximum time that excess idle threads - * will wait for new tasks before terminating. - * @param unit the time unit for the keepAliveTime - * argument. - * @param workQueue the queue to use for holding tasks before they - * are executed. This queue will hold only the <tt>Runnable</tt> - * tasks submitted by the <tt>execute</tt> method. + * the core, this is the maximum time that excess idle threads + * will wait for new tasks before terminating. + * @param unit the time unit for the {@code keepAliveTime} argument + * @param workQueue the queue to use for holding tasks before they are + * executed. This queue will hold only the {@code Runnable} + * tasks submitted by the {@code execute} method. * @param handler the handler to use when execution is blocked - * because the thread bounds and queue capacities are reached. - * @throws IllegalArgumentException if corePoolSize, or - * keepAliveTime less than zero, or if maximumPoolSize less than or - * equal to zero, or if corePoolSize greater than maximumPoolSize. - * @throws NullPointerException if <tt>workQueue</tt> - * or <tt>handler</tt> are null. + * because the thread bounds and queue capacities are reached + * @throws IllegalArgumentException if one of the following holds:<br> + * {@code corePoolSize < 0}<br> + * {@code keepAliveTime < 0}<br> + * {@code maximumPoolSize <= 0}<br> + * {@code maximumPoolSize < corePoolSize} + * @throws NullPointerException if {@code workQueue} + * or {@code handler} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, @@ -784,30 +1193,31 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } /** - * Creates a new <tt>ThreadPoolExecutor</tt> with the given initial + * Creates a new {@code ThreadPoolExecutor} with the given initial * parameters. * - * @param corePoolSize the number of threads to keep in the - * pool, even if they are idle. + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle * @param maximumPoolSize the maximum number of threads to allow in the - * pool. + * pool * @param keepAliveTime when the number of threads is greater than - * the core, this is the maximum time that excess idle threads - * will wait for new tasks before terminating. - * @param unit the time unit for the keepAliveTime - * argument. - * @param workQueue the queue to use for holding tasks before they - * are executed. This queue will hold only the <tt>Runnable</tt> - * tasks submitted by the <tt>execute</tt> method. + * the core, this is the maximum time that excess idle threads + * will wait for new tasks before terminating. + * @param unit the time unit for the {@code keepAliveTime} argument + * @param workQueue the queue to use for holding tasks before they are + * executed. This queue will hold only the {@code Runnable} + * tasks submitted by the {@code execute} method. * @param threadFactory the factory to use when the executor - * creates a new thread. + * creates a new thread * @param handler the handler to use when execution is blocked - * because the thread bounds and queue capacities are reached. - * @throws IllegalArgumentException if corePoolSize, or - * keepAliveTime less than zero, or if maximumPoolSize less than or - * equal to zero, or if corePoolSize greater than maximumPoolSize. - * @throws NullPointerException if <tt>workQueue</tt> - * or <tt>threadFactory</tt> or <tt>handler</tt> are null. + * because the thread bounds and queue capacities are reached + * @throws IllegalArgumentException if one of the following holds:<br> + * {@code corePoolSize < 0}<br> + * {@code keepAliveTime < 0}<br> + * {@code maximumPoolSize <= 0}<br> + * {@code maximumPoolSize < corePoolSize} + * @throws NullPointerException if {@code workQueue} + * or {@code threadFactory} or {@code handler} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, @@ -831,181 +1241,140 @@ public class ThreadPoolExecutor extends AbstractExecutorService { this.handler = handler; } - /** * Executes the given task sometime in the future. The task * may execute in a new thread or in an existing pooled thread. * * If the task cannot be submitted for execution, either because this * executor has been shutdown or because its capacity has been reached, - * the task is handled by the current <tt>RejectedExecutionHandler</tt>. + * the task is handled by the current {@code RejectedExecutionHandler}. * * @param command the task to execute * @throws RejectedExecutionException at discretion of - * <tt>RejectedExecutionHandler</tt>, if task cannot be accepted - * for execution - * @throws NullPointerException if command is null + * {@code RejectedExecutionHandler}, if the task + * cannot be accepted for execution + * @throws NullPointerException if {@code command} is null */ public void execute(Runnable command) { if (command == null) throw new NullPointerException(); - for (;;) { - if (runState != RUNNING) { - reject(command); - return; - } - if (poolSize < corePoolSize && addIfUnderCorePoolSize(command)) - return; - if (workQueue.offer(command)) - return; - Runnable r = addIfUnderMaximumPoolSize(command); - if (r == command) + /* + * Proceed in 3 steps: + * + * 1. If fewer than corePoolSize threads are running, try to + * start a new thread with the given command as its first + * task. The call to addWorker atomically checks runState and + * workerCount, and so prevents false alarms that would add + * threads when it shouldn't, by returning false. + * + * 2. If a task can be successfully queued, then we still need + * to double-check whether we should have added a thread + * (because existing ones died since last checking) or that + * the pool shut down since entry into this method. So we + * recheck state and if necessary roll back the enqueuing if + * stopped, or start a new thread if there are none. + * + * 3. If we cannot queue task, then we try to add a new + * thread. If it fails, we know we are shut down or saturated + * and so reject the task. + */ + int c = ctl.get(); + if (workerCountOf(c) < corePoolSize) { + if (addWorker(command, true)) return; - if (r == null) { + c = ctl.get(); + } + if (isRunning(c) && workQueue.offer(command)) { + int recheck = ctl.get(); + if (! isRunning(recheck) && remove(command)) reject(command); - return; - } - // else retry + else if (workerCountOf(recheck) == 0) + addWorker(null, false); } + else if (!addWorker(command, false)) + reject(command); } /** * Initiates an orderly shutdown in which previously submitted - * tasks are executed, but no new tasks will be - * accepted. Invocation has no additional effect if already shut - * down. - * @throws SecurityException if a security manager exists and - * shutting down this ExecutorService may manipulate threads that - * the caller is not permitted to modify because it does not hold - * {@link java.lang.RuntimePermission}<tt>("modifyThread")</tt>, - * or the security manager's <tt>checkAccess</tt> method denies access. + * tasks are executed, but no new tasks will be accepted. + * Invocation has no additional effect if already shut down. + * + * <p>This method does not wait for previously submitted tasks to + * complete execution. Use {@link #awaitTermination awaitTermination} + * to do that. + * + * @throws SecurityException {@inheritDoc} */ public void shutdown() { - // Fail if caller doesn't have modifyThread permission - SecurityManager security = System.getSecurityManager(); - if (security != null) - java.security.AccessController.checkPermission(shutdownPerm); - - boolean fullyTerminated = false; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { - if (workers.size() > 0) { - // Check if caller can modify worker threads. This - // might not be true even if passed above check, if - // the SecurityManager treats some threads specially. - if (security != null) { - for (Worker w: workers) - security.checkAccess(w.thread); - } - - int state = runState; - if (state == RUNNING) // don't override shutdownNow - runState = SHUTDOWN; - - try { - for (Worker w: workers) - w.interruptIfIdle(); - } catch(SecurityException se) { - // If SecurityManager allows above checks, but - // then unexpectedly throws exception when - // interrupting threads (which it ought not do), - // back out as cleanly as we can. Some threads may - // have been killed but we remain in non-shutdown - // state. - runState = state; - throw se; - } - } - else { // If no workers, trigger full termination now - fullyTerminated = true; - runState = TERMINATED; - termination.signalAll(); - } + checkShutdownAccess(); + advanceRunState(SHUTDOWN); + interruptIdleWorkers(); + onShutdown(); // hook for ScheduledThreadPoolExecutor } finally { mainLock.unlock(); } - if (fullyTerminated) - terminated(); + tryTerminate(); } - /** * Attempts to stop all actively executing tasks, halts the - * processing of waiting tasks, and returns a list of the tasks that were - * awaiting execution. - * - * <p>This implementation cancels tasks via {@link - * Thread#interrupt}, so if any tasks mask or fail to respond to - * interrupts, they may never terminate. + * processing of waiting tasks, and returns a list of the tasks + * that were awaiting execution. These tasks are drained (removed) + * from the task queue upon return from this method. * - * @return list of tasks that never commenced execution - * @throws SecurityException if a security manager exists and - * shutting down this ExecutorService may manipulate threads that - * the caller is not permitted to modify because it does not hold - * {@link java.lang.RuntimePermission}<tt>("modifyThread")</tt>, - * or the security manager's <tt>checkAccess</tt> method denies access. + * <p>This method does not wait for actively executing tasks to + * terminate. Use {@link #awaitTermination awaitTermination} to + * do that. + * + * <p>There are no guarantees beyond best-effort attempts to stop + * processing actively executing tasks. This implementation + * cancels tasks via {@link Thread#interrupt}, so any task that + * fails to respond to interrupts may never terminate. + * + * @throws SecurityException {@inheritDoc} */ public List<Runnable> shutdownNow() { - // Almost the same code as shutdown() - SecurityManager security = System.getSecurityManager(); - if (security != null) - java.security.AccessController.checkPermission(shutdownPerm); - - boolean fullyTerminated = false; + List<Runnable> tasks; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { - if (workers.size() > 0) { - if (security != null) { - for (Worker w: workers) - security.checkAccess(w.thread); - } - - int state = runState; - if (state != TERMINATED) - runState = STOP; - try { - for (Worker w : workers) - w.interruptNow(); - } catch(SecurityException se) { - runState = state; // back out; - throw se; - } - } - else { // If no workers, trigger full termination now - fullyTerminated = true; - runState = TERMINATED; - termination.signalAll(); - } + checkShutdownAccess(); + advanceRunState(STOP); + interruptWorkers(); + tasks = drainQueue(); } finally { mainLock.unlock(); } - if (fullyTerminated) - terminated(); - return Arrays.asList(workQueue.toArray(EMPTY_RUNNABLE_ARRAY)); + tryTerminate(); + return tasks; } public boolean isShutdown() { - return runState != RUNNING; + return ! isRunning(ctl.get()); } - /** + /** * Returns true if this executor is in the process of terminating - * after <tt>shutdown</tt> or <tt>shutdownNow</tt> but has not + * after {@link #shutdown} or {@link #shutdownNow} but has not * completely terminated. This method may be useful for - * debugging. A return of <tt>true</tt> reported a sufficient + * debugging. A return of {@code true} reported a sufficient * period after shutdown may indicate that submitted tasks have * ignored or suppressed interruption, causing this executor not * to properly terminate. - * @return true if terminating but not yet terminated. + * + * @return true if terminating but not yet terminated */ public boolean isTerminating() { - return runState == STOP; + int c = ctl.get(); + return ! isRunning(c) && runStateLessThan(c, TERMINATED); } public boolean isTerminated() { - return runState == TERMINATED; + return runStateAtLeast(ctl.get(), TERMINATED); } public boolean awaitTermination(long timeout, TimeUnit unit) @@ -1015,7 +1384,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService { mainLock.lock(); try { for (;;) { - if (runState == TERMINATED) + if (runStateAtLeast(ctl.get(), TERMINATED)) return true; if (nanos <= 0) return false; @@ -1027,10 +1396,10 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } /** - * Invokes <tt>shutdown</tt> when this executor is no longer - * referenced. - */ - protected void finalize() { + * Invokes {@code shutdown} when this executor is no longer + * referenced and it has no threads. + */ + protected void finalize() { shutdown(); } @@ -1081,103 +1450,33 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } /** - * Returns the task queue used by this executor. Access to the - * task queue is intended primarily for debugging and monitoring. - * This queue may be in active use. Retrieving the task queue - * does not prevent queued tasks from executing. - * - * @return the task queue - */ - public BlockingQueue<Runnable> getQueue() { - return workQueue; - } - - /** - * Removes this task from the executor's internal queue if it is - * present, thus causing it not to be run if it has not already - * started. - * - * <p> This method may be useful as one part of a cancellation - * scheme. It may fail to remove tasks that have been converted - * into other forms before being placed on the internal queue. For - * example, a task entered using <tt>submit</tt> might be - * converted into a form that maintains <tt>Future</tt> status. - * However, in such cases, method {@link ThreadPoolExecutor#purge} - * may be used to remove those Futures that have been cancelled. - * - * - * @param task the task to remove - * @return true if the task was removed - */ - public boolean remove(Runnable task) { - return getQueue().remove(task); - } - - - /** - * Tries to remove from the work queue all {@link Future} - * tasks that have been cancelled. This method can be useful as a - * storage reclamation operation, that has no other impact on - * functionality. Cancelled tasks are never executed, but may - * accumulate in work queues until worker threads can actively - * remove them. Invoking this method instead tries to remove them now. - * However, this method may fail to remove tasks in - * the presence of interference by other threads. - */ - public void purge() { - // Fail if we encounter interference during traversal - try { - Iterator<Runnable> it = getQueue().iterator(); - while (it.hasNext()) { - Runnable r = it.next(); - if (r instanceof Future<?>) { - Future<?> c = (Future<?>)r; - if (c.isCancelled()) - it.remove(); - } - } - } - catch(ConcurrentModificationException ex) { - return; - } - } - - /** * Sets the core number of threads. This overrides any value set * in the constructor. If the new value is smaller than the * current value, excess existing threads will be terminated when - * they next become idle. If larger, new threads will, if needed, + * they next become idle. If larger, new threads will, if needed, * be started to execute any queued tasks. * * @param corePoolSize the new core size - * @throws IllegalArgumentException if <tt>corePoolSize</tt> - * less than zero + * @throws IllegalArgumentException if {@code corePoolSize < 0} * @see #getCorePoolSize */ public void setCorePoolSize(int corePoolSize) { if (corePoolSize < 0) throw new IllegalArgumentException(); - final ReentrantLock mainLock = this.mainLock; - mainLock.lock(); - try { - int extra = this.corePoolSize - corePoolSize; - this.corePoolSize = corePoolSize; - if (extra < 0) { - Runnable r; - while (extra++ < 0 && poolSize < corePoolSize && - (r = workQueue.poll()) != null) - addThread(r).start(); - } - else if (extra > 0 && poolSize > corePoolSize) { - Iterator<Worker> it = workers.iterator(); - while (it.hasNext() && - extra-- > 0 && - poolSize > corePoolSize && - workQueue.remainingCapacity() == 0) - it.next().interruptIfIdle(); + int delta = corePoolSize - this.corePoolSize; + this.corePoolSize = corePoolSize; + if (workerCountOf(ctl.get()) > corePoolSize) + interruptIdleWorkers(); + else if (delta > 0) { + // We don't really know how many new threads are "needed". + // As a heuristic, prestart enough new workers (up to new + // core size) to handle the current number of tasks in + // queue, but stop if queue becomes empty while doing so. + int k = Math.min(delta, workQueue.size()); + while (k-- > 0 && addWorker(null, true)) { + if (workQueue.isEmpty()) + break; } - } finally { - mainLock.unlock(); } } @@ -1194,23 +1493,26 @@ public class ThreadPoolExecutor extends AbstractExecutorService { /** * Starts a core thread, causing it to idly wait for work. This * overrides the default policy of starting core threads only when - * new tasks are executed. This method will return <tt>false</tt> + * new tasks are executed. This method will return {@code false} * if all core threads have already been started. - * @return true if a thread was started - */ + * + * @return {@code true} if a thread was started + */ public boolean prestartCoreThread() { - return addIfUnderCorePoolSize(null); + return workerCountOf(ctl.get()) < corePoolSize && + addWorker(null, true); } /** * Starts all core threads, causing them to idly wait for work. This * overrides the default policy of starting core threads only when - * new tasks are executed. - * @return the number of threads started. - */ + * new tasks are executed. + * + * @return the number of threads started + */ public int prestartAllCoreThreads() { int n = 0; - while (addIfUnderCorePoolSize(null)) + while (addWorker(null, true)) ++n; return n; } @@ -1222,30 +1524,17 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * terminated when they next become idle. * * @param maximumPoolSize the new maximum - * @throws IllegalArgumentException if maximumPoolSize less than zero or - * the {@link #getCorePoolSize core pool size} + * @throws IllegalArgumentException if the new maximum is + * less than or equal to zero, or + * less than the {@linkplain #getCorePoolSize core pool size} * @see #getMaximumPoolSize */ public void setMaximumPoolSize(int maximumPoolSize) { if (maximumPoolSize <= 0 || maximumPoolSize < corePoolSize) throw new IllegalArgumentException(); - final ReentrantLock mainLock = this.mainLock; - mainLock.lock(); - try { - int extra = this.maximumPoolSize - maximumPoolSize; - this.maximumPoolSize = maximumPoolSize; - if (extra > 0 && poolSize > maximumPoolSize) { - Iterator<Worker> it = workers.iterator(); - while (it.hasNext() && - extra > 0 && - poolSize > maximumPoolSize) { - it.next().interruptIfIdle(); - --extra; - } - } - } finally { - mainLock.unlock(); - } + this.maximumPoolSize = maximumPoolSize; + if (workerCountOf(ctl.get()) > maximumPoolSize) + interruptIdleWorkers(); } /** @@ -1264,21 +1553,27 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * threads currently in the pool, after waiting this amount of * time without processing a task, excess threads will be * terminated. This overrides any value set in the constructor. + * * @param time the time to wait. A time value of zero will cause - * excess threads to terminate immediately after executing tasks. - * @param unit the time unit of the time argument - * @throws IllegalArgumentException if time less than zero + * excess threads to terminate immediately after executing tasks. + * @param unit the time unit of the {@code time} argument + * @throws IllegalArgumentException if {@code time} less than zero or + * if {@code time} is zero and {@code allowsCoreThreadTimeOut} * @see #getKeepAliveTime */ public void setKeepAliveTime(long time, TimeUnit unit) { if (time < 0) throw new IllegalArgumentException(); - this.keepAliveTime = unit.toNanos(time); + long keepAliveTime = unit.toNanos(time); + long delta = keepAliveTime - this.keepAliveTime; + this.keepAliveTime = keepAliveTime; + if (delta < 0) + interruptIdleWorkers(); } /** * Returns the thread keep-alive time, which is the amount of time - * which threads in excess of the core pool size may remain + * that threads in excess of the core pool size may remain * idle before being terminated. * * @param unit the desired time unit of the result @@ -1289,6 +1584,73 @@ public class ThreadPoolExecutor extends AbstractExecutorService { return unit.convert(keepAliveTime, TimeUnit.NANOSECONDS); } + /* User-level queue utilities */ + + /** + * Returns the task queue used by this executor. Access to the + * task queue is intended primarily for debugging and monitoring. + * This queue may be in active use. Retrieving the task queue + * does not prevent queued tasks from executing. + * + * @return the task queue + */ + public BlockingQueue<Runnable> getQueue() { + return workQueue; + } + + /** + * Removes this task from the executor's internal queue if it is + * present, thus causing it not to be run if it has not already + * started. + * + * <p> This method may be useful as one part of a cancellation + * scheme. It may fail to remove tasks that have been converted + * into other forms before being placed on the internal queue. For + * example, a task entered using {@code submit} might be + * converted into a form that maintains {@code Future} status. + * However, in such cases, method {@link #purge} may be used to + * remove those Futures that have been cancelled. + * + * @param task the task to remove + * @return true if the task was removed + */ + public boolean remove(Runnable task) { + boolean removed = workQueue.remove(task); + tryTerminate(); // In case SHUTDOWN and now empty + return removed; + } + + /** + * Tries to remove from the work queue all {@link Future} + * tasks that have been cancelled. This method can be useful as a + * storage reclamation operation, that has no other impact on + * functionality. Cancelled tasks are never executed, but may + * accumulate in work queues until worker threads can actively + * remove them. Invoking this method instead tries to remove them now. + * However, this method may fail to remove tasks in + * the presence of interference by other threads. + */ + public void purge() { + final BlockingQueue<Runnable> q = workQueue; + try { + Iterator<Runnable> it = q.iterator(); + while (it.hasNext()) { + Runnable r = it.next(); + if (r instanceof Future<?> && ((Future<?>)r).isCancelled()) + it.remove(); + } + } catch (ConcurrentModificationException fallThrough) { + // Take slow path if we encounter interference during traversal. + // Make copy for traversal and call remove for cancelled entries. + // The slow path is more likely to be O(N*N). + for (Object r : q.toArray()) + if (r instanceof Future<?> && ((Future<?>)r).isCancelled()) + q.remove(r); + } + + tryTerminate(); // In case SHUTDOWN and now empty + } + /* Statistics */ /** @@ -1297,7 +1659,16 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * @return the number of threads */ public int getPoolSize() { - return poolSize; + final ReentrantLock mainLock = this.mainLock; + mainLock.lock(); + try { + // Remove rare and surprising possibility of + // isTerminated() && getPoolSize() > 0 + return runStateAtLeast(ctl.get(), TIDYING) ? 0 + : workers.size(); + } finally { + mainLock.unlock(); + } } /** @@ -1311,10 +1682,9 @@ public class ThreadPoolExecutor extends AbstractExecutorService { mainLock.lock(); try { int n = 0; - for (Worker w : workers) { - if (w.isActive()) + for (Worker w : workers) + if (w.isLocked()) ++n; - } return n; } finally { mainLock.unlock(); @@ -1338,11 +1708,10 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } /** - * Returns the approximate total number of tasks that have been + * Returns the approximate total number of tasks that have ever been * scheduled for execution. Because the states of tasks and * threads may change dynamically during computation, the returned - * value is only an approximation, but one that does not ever - * decrease across successive calls. + * value is only an approximation. * * @return the number of tasks */ @@ -1353,7 +1722,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService { long n = completedTaskCount; for (Worker w : workers) { n += w.completedTasks; - if (w.isActive()) + if (w.isLocked()) ++n; } return n + workQueue.size(); @@ -1384,30 +1753,69 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } } + /* Extension hooks */ + /** * Method invoked prior to executing the given Runnable in the - * given thread. This method is invoked by thread <tt>t</tt> that - * will execute task <tt>r</tt>, and may be used to re-initialize - * ThreadLocals, or to perform logging. Note: To properly nest - * multiple overridings, subclasses should generally invoke - * <tt>super.beforeExecute</tt> at the end of this method. + * given thread. This method is invoked by thread {@code t} that + * will execute task {@code r}, and may be used to re-initialize + * ThreadLocals, or to perform logging. + * + * <p>This implementation does nothing, but may be customized in + * subclasses. Note: To properly nest multiple overridings, subclasses + * should generally invoke {@code super.beforeExecute} at the end of + * this method. * - * @param t the thread that will run task r. - * @param r the task that will be executed. + * @param t the thread that will run task {@code r} + * @param r the task that will be executed */ protected void beforeExecute(Thread t, Runnable r) { } /** - * Method invoked upon completion of execution of the given - * Runnable. This method is invoked by the thread that executed - * the task. If non-null, the Throwable is the uncaught exception - * that caused execution to terminate abruptly. Note: To properly - * nest multiple overridings, subclasses should generally invoke - * <tt>super.afterExecute</tt> at the beginning of this method. + * Method invoked upon completion of execution of the given Runnable. + * This method is invoked by the thread that executed the task. If + * non-null, the Throwable is the uncaught {@code RuntimeException} + * or {@code Error} that caused execution to terminate abruptly. + * + * <p>This implementation does nothing, but may be customized in + * subclasses. Note: To properly nest multiple overridings, subclasses + * should generally invoke {@code super.afterExecute} at the + * beginning of this method. * - * @param r the runnable that has completed. + * <p><b>Note:</b> When actions are enclosed in tasks (such as + * {@link FutureTask}) either explicitly or via methods such as + * {@code submit}, these task objects catch and maintain + * computational exceptions, and so they do not cause abrupt + * termination, and the internal exceptions are <em>not</em> + * passed to this method. If you would like to trap both kinds of + * failures in this method, you can further probe for such cases, + * as in this sample subclass that prints either the direct cause + * or the underlying exception if a task has been aborted: + * + * <pre> {@code + * class ExtendedExecutor extends ThreadPoolExecutor { + * // ... + * protected void afterExecute(Runnable r, Throwable t) { + * super.afterExecute(r, t); + * if (t == null && r instanceof Future<?>) { + * try { + * Object result = ((Future<?>) r).get(); + * } catch (CancellationException ce) { + * t = ce; + * } catch (ExecutionException ee) { + * t = ee.getCause(); + * } catch (InterruptedException ie) { + * Thread.currentThread().interrupt(); // ignore/reset + * } + * } + * if (t != null) + * System.out.println(t); + * } + * }}</pre> + * + * @param r the runnable that has completed * @param t the exception that caused termination, or null if - * execution completed normally. + * execution completed normally */ protected void afterExecute(Runnable r, Throwable t) { } @@ -1415,25 +1823,28 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * Method invoked when the Executor has terminated. Default * implementation does nothing. Note: To properly nest multiple * overridings, subclasses should generally invoke - * <tt>super.terminated</tt> within this method. + * {@code super.terminated} within this method. */ protected void terminated() { } + /* Predefined RejectedExecutionHandlers */ + /** * A handler for rejected tasks that runs the rejected task - * directly in the calling thread of the <tt>execute</tt> method, + * directly in the calling thread of the {@code execute} method, * unless the executor has been shut down, in which case the task * is discarded. */ - public static class CallerRunsPolicy implements RejectedExecutionHandler { + public static class CallerRunsPolicy implements RejectedExecutionHandler { /** - * Creates a <tt>CallerRunsPolicy</tt>. + * Creates a {@code CallerRunsPolicy}. */ public CallerRunsPolicy() { } /** * Executes task r in the caller's thread, unless the executor * has been shut down, in which case the task is discarded. + * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task */ @@ -1446,16 +1857,17 @@ public class ThreadPoolExecutor extends AbstractExecutorService { /** * A handler for rejected tasks that throws a - * <tt>RejectedExecutionException</tt>. + * {@code RejectedExecutionException}. */ public static class AbortPolicy implements RejectedExecutionHandler { /** - * Creates an <tt>AbortPolicy</tt>. + * Creates an {@code AbortPolicy}. */ public AbortPolicy() { } /** * Always throws RejectedExecutionException. + * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task * @throws RejectedExecutionException always. @@ -1471,12 +1883,13 @@ public class ThreadPoolExecutor extends AbstractExecutorService { */ public static class DiscardPolicy implements RejectedExecutionHandler { /** - * Creates a <tt>DiscardPolicy</tt>. + * Creates a {@code DiscardPolicy}. */ public DiscardPolicy() { } /** * Does nothing, which has the effect of discarding task r. + * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task */ @@ -1486,12 +1899,12 @@ public class ThreadPoolExecutor extends AbstractExecutorService { /** * A handler for rejected tasks that discards the oldest unhandled - * request and then retries <tt>execute</tt>, unless the executor + * request and then retries {@code execute}, unless the executor * is shut down, in which case the task is discarded. */ public static class DiscardOldestPolicy implements RejectedExecutionHandler { /** - * Creates a <tt>DiscardOldestPolicy</tt> for the given executor. + * Creates a {@code DiscardOldestPolicy} for the given executor. */ public DiscardOldestPolicy() { } @@ -1500,6 +1913,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * would otherwise execute, if one is immediately available, * and then retries execution of task r, unless the executor * is shut down, in which case task r is instead discarded. + * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task */ diff --git a/concurrent/src/main/java/java/util/concurrent/TimeUnit.java b/concurrent/src/main/java/java/util/concurrent/TimeUnit.java index b186aeb..412d28a 100644 --- a/concurrent/src/main/java/java/util/concurrent/TimeUnit.java +++ b/concurrent/src/main/java/java/util/concurrent/TimeUnit.java @@ -12,7 +12,11 @@ package java.util.concurrent; * and to perform timing and delay operations in these units. A * <tt>TimeUnit</tt> does not maintain time information, but only * helps organize and use time representations that may be maintained - * separately across various contexts. + * separately across various contexts. A nanosecond is defined as one + * thousandth of a microsecond, a microsecond as one thousandth of a + * millisecond, a millisecond as one thousandth of a second, a minute + * as sixty seconds, an hour as sixty minutes, and a day as twenty four + * hours. * * <p>A <tt>TimeUnit</tt> is mainly used to inform time-based methods * how a given timing parameter should be interpreted. For example, @@ -37,61 +41,80 @@ package java.util.concurrent; */ public enum TimeUnit { /** TimeUnit which represents one nanosecond. */ - NANOSECONDS(0), + NANOSECONDS { + public long toNanos(long d) { return d; } + public long toMicros(long d) { return d/(C1/C0); } + public long toMillis(long d) { return d/(C2/C0); } + public long toSeconds(long d) { return d/(C3/C0); } + public long toMinutes(long d) { return d/(C4/C0); } + public long toHours(long d) { return d/(C5/C0); } + public long toDays(long d) { return d/(C6/C0); } + public long convert(long d, TimeUnit u) { return u.toNanos(d); } + int excessNanos(long d, long m) { return (int)(d - (m*C2)); } + }, /** TimeUnit which represents one microsecond. */ - MICROSECONDS(1), + MICROSECONDS { + public long toNanos(long d) { return x(d, C1/C0, MAX/(C1/C0)); } + public long toMicros(long d) { return d; } + public long toMillis(long d) { return d/(C2/C1); } + public long toSeconds(long d) { return d/(C3/C1); } + public long toMinutes(long d) { return d/(C4/C1); } + public long toHours(long d) { return d/(C5/C1); } + public long toDays(long d) { return d/(C6/C1); } + public long convert(long d, TimeUnit u) { return u.toMicros(d); } + int excessNanos(long d, long m) { return (int)((d*C1) - (m*C2)); } + }, /** TimeUnit which represents one millisecond. */ - MILLISECONDS(2), + MILLISECONDS { + public long toNanos(long d) { return x(d, C2/C0, MAX/(C2/C0)); } + public long toMicros(long d) { return x(d, C2/C1, MAX/(C2/C1)); } + public long toMillis(long d) { return d; } + public long toSeconds(long d) { return d/(C3/C2); } + public long toMinutes(long d) { return d/(C4/C2); } + public long toHours(long d) { return d/(C5/C2); } + public long toDays(long d) { return d/(C6/C2); } + public long convert(long d, TimeUnit u) { return u.toMillis(d); } + int excessNanos(long d, long m) { return 0; } + }, /** TimeUnit which represents one second. */ - SECONDS(3); + SECONDS { + public long toNanos(long d) { return x(d, C3/C0, MAX/(C3/C0)); } + public long toMicros(long d) { return x(d, C3/C1, MAX/(C3/C1)); } + public long toMillis(long d) { return x(d, C3/C2, MAX/(C3/C2)); } + public long toSeconds(long d) { return d; } + public long toMinutes(long d) { return d/(C4/C3); } + public long toHours(long d) { return d/(C5/C3); } + public long toDays(long d) { return d/(C6/C3); } + public long convert(long d, TimeUnit u) { return u.toSeconds(d); } + int excessNanos(long d, long m) { return 0; } + }; - /** the index of this unit */ - private final int index; + // Handy constants for conversion methods + static final long C0 = 1L; + static final long C1 = C0 * 1000L; + static final long C2 = C1 * 1000L; + static final long C3 = C2 * 1000L; + static final long C4 = C3 * 60L; + static final long C5 = C4 * 60L; + static final long C6 = C5 * 24L; - /** Internal constructor */ - TimeUnit(int index) { - this.index = index; - } - - /** Lookup table for conversion factors */ - private static final int[] multipliers = { - 1, - 1000, - 1000 * 1000, - 1000 * 1000 * 1000 - }; - - /** - * Lookup table to check saturation. Note that because we are - * dividing these down, we don't have to deal with asymmetry of - * MIN/MAX values. - */ - private static final long[] overflows = { - 0, // unused - Long.MAX_VALUE / 1000, - Long.MAX_VALUE / (1000 * 1000), - Long.MAX_VALUE / (1000 * 1000 * 1000) - }; + static final long MAX = Long.MAX_VALUE; /** - * Perform conversion based on given delta representing the - * difference between units - * @param delta the difference in index values of source and target units - * @param duration the duration - * @return converted duration or saturated value + * Scale d by m, checking for overflow. + * This has a short name to make above code more readable. */ - private static long doConvert(int delta, long duration) { - if (delta == 0) - return duration; - if (delta < 0) - return duration / multipliers[-delta]; - if (duration > overflows[delta]) - return Long.MAX_VALUE; - if (duration < -overflows[delta]) - return Long.MIN_VALUE; - return duration * multipliers[delta]; + static long x(long d, long m, long over) { + if (d > over) return Long.MAX_VALUE; + if (d < -over) return Long.MIN_VALUE; + return d * m; } + // To maintain full signature compatibility with 1.5, and to improve the + // clarity of the generated javadoc (see 6287639: Abstract methods in + // enum classes should not be listed as abstract), method convert + // etc. are not declared abstract but otherwise act as abstract methods. + /** * Convert the given time duration in the given unit to this * unit. Conversions from finer to coarser granularities @@ -102,14 +125,17 @@ public enum TimeUnit { * <tt>Long.MIN_VALUE</tt> if negative or <tt>Long.MAX_VALUE</tt> * if positive. * - * @param duration the time duration in the given <tt>unit</tt> - * @param unit the unit of the <tt>duration</tt> argument + * <p>For example, to convert 10 minutes to milliseconds, use: + * <tt>TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES)</tt> + * + * @param sourceDuration the time duration in the given <tt>sourceUnit</tt> + * @param sourceUnit the unit of the <tt>sourceDuration</tt> argument * @return the converted duration in this unit, * or <tt>Long.MIN_VALUE</tt> if conversion would negatively * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow. */ - public long convert(long duration, TimeUnit unit) { - return doConvert(unit.index - index, duration); + public long convert(long sourceDuration, TimeUnit sourceUnit) { + throw new AbstractMethodError(); } /** @@ -121,7 +147,7 @@ public enum TimeUnit { * @see #convert */ public long toNanos(long duration) { - return doConvert(index, duration); + throw new AbstractMethodError(); } /** @@ -133,7 +159,7 @@ public enum TimeUnit { * @see #convert */ public long toMicros(long duration) { - return doConvert(index - MICROSECONDS.index, duration); + throw new AbstractMethodError(); } /** @@ -145,34 +171,66 @@ public enum TimeUnit { * @see #convert */ public long toMillis(long duration) { - return doConvert(index - MILLISECONDS.index, duration); + throw new AbstractMethodError(); } /** * Equivalent to <tt>SECONDS.convert(duration, this)</tt>. * @param duration the duration - * @return the converted duration. + * @return the converted duration, + * or <tt>Long.MIN_VALUE</tt> if conversion would negatively + * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow. * @see #convert */ public long toSeconds(long duration) { - return doConvert(index - SECONDS.index, duration); + throw new AbstractMethodError(); + } + + /** + * Equivalent to <tt>MINUTES.convert(duration, this)</tt>. + * @param duration the duration + * @return the converted duration, + * or <tt>Long.MIN_VALUE</tt> if conversion would negatively + * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow. + * @see #convert + */ + long toMinutes(long duration) { + throw new AbstractMethodError(); } + /** + * Equivalent to <tt>HOURS.convert(duration, this)</tt>. + * @param duration the duration + * @return the converted duration, + * or <tt>Long.MIN_VALUE</tt> if conversion would negatively + * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow. + * @see #convert + */ + long toHours(long duration) { + throw new AbstractMethodError(); + } /** - * Utility method to compute the excess-nanosecond argument to - * wait, sleep, join. + * Equivalent to <tt>DAYS.convert(duration, this)</tt>. + * @param duration the duration + * @return the converted duration + * @see #convert */ - private int excessNanos(long time, long ms) { - if (this == NANOSECONDS) - return (int) (time - (ms * 1000 * 1000)); - if (this == MICROSECONDS) - return (int) ((time * 1000) - (ms * 1000 * 1000)); - return 0; + long toDays(long duration) { + throw new AbstractMethodError(); } /** - * Perform a timed <tt>Object.wait</tt> using this time unit. + * Utility to compute the excess-nanosecond argument to wait, + * sleep, join. + * @param d the duration + * @param m the number of milliseconds + * @return the number of nanoseconds + */ + abstract int excessNanos(long d, long m); + + /** + * Performs a timed <tt>Object.wait</tt> using this time unit. * This is a convenience method that converts timeout arguments * into the form required by the <tt>Object.wait</tt> method. * @@ -180,7 +238,7 @@ public enum TimeUnit { * method (see {@link BlockingQueue#poll BlockingQueue.poll}) * using: * - * <pre> public synchronized Object poll(long timeout, TimeUnit unit) throws InterruptedException { + * <pre> public synchronized Object poll(long timeout, TimeUnit unit) throws InterruptedException { * while (empty) { * unit.timedWait(this, timeout); * ... @@ -188,12 +246,13 @@ public enum TimeUnit { * }</pre> * * @param obj the object to wait on - * @param timeout the maximum time to wait. + * @param timeout the maximum time to wait. If less than + * or equal to zero, do not wait at all. * @throws InterruptedException if interrupted while waiting. * @see Object#wait(long, int) */ public void timedWait(Object obj, long timeout) - throws InterruptedException { + throws InterruptedException { if (timeout > 0) { long ms = toMillis(timeout); int ns = excessNanos(timeout, ms); @@ -202,16 +261,17 @@ public enum TimeUnit { } /** - * Perform a timed <tt>Thread.join</tt> using this time unit. + * Performs a timed <tt>Thread.join</tt> using this time unit. * This is a convenience method that converts time arguments into the * form required by the <tt>Thread.join</tt> method. * @param thread the thread to wait for - * @param timeout the maximum time to wait + * @param timeout the maximum time to wait. If less than + * or equal to zero, do not wait at all. * @throws InterruptedException if interrupted while waiting. * @see Thread#join(long, int) */ public void timedJoin(Thread thread, long timeout) - throws InterruptedException { + throws InterruptedException { if (timeout > 0) { long ms = toMillis(timeout); int ns = excessNanos(timeout, ms); @@ -220,10 +280,11 @@ public enum TimeUnit { } /** - * Perform a <tt>Thread.sleep</tt> using this unit. + * Performs a <tt>Thread.sleep</tt> using this unit. * This is a convenience method that converts time arguments into the * form required by the <tt>Thread.sleep</tt> method. - * @param timeout the minimum time to sleep + * @param timeout the minimum time to sleep. If less than + * or equal to zero, do not sleep at all. * @throws InterruptedException if interrupted while sleeping. * @see Thread#sleep */ diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java index fdd3d49..ba60556 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java @@ -8,10 +8,10 @@ package java.util.concurrent.atomic; import sun.misc.Unsafe; /** - * A <tt>boolean</tt> value that may be updated atomically. See the + * A {@code boolean} value that may be updated atomically. See the * {@link java.util.concurrent.atomic} package specification for * description of the properties of atomic variables. An - * <tt>AtomicBoolean</tt> is used in applications such as atomically + * {@code AtomicBoolean} is used in applications such as atomically * updated flags, and cannot be used as a replacement for a * {@link java.lang.Boolean}. * @@ -37,7 +37,7 @@ public class AtomicBoolean implements java.io.Serializable { private volatile int value; /** - * Creates a new <tt>AtomicBoolean</tt> with the given initial value. + * Creates a new {@code AtomicBoolean} with the given initial value. * * @param initialValue the initial value */ @@ -46,7 +46,7 @@ public class AtomicBoolean implements java.io.Serializable { } /** - * Creates a new <tt>AtomicBoolean</tt> with initial value <tt>false</tt>. + * Creates a new {@code AtomicBoolean} with initial value {@code false}. */ public AtomicBoolean() { } @@ -61,16 +61,13 @@ public class AtomicBoolean implements java.io.Serializable { } /** - * Atomically sets the value to the given update value if the - * current value is equal to the expected value. Any given - * invocation of this operation may fail (return - * <tt>false</tt>) spuriously, but repeated invocation when - * the current value holds the expected value and no other thread - * is also attempting to set the value will eventually succeed. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. * * @param expect the expected value * @param update the new value - * @return true if successful + * @return true if successful. False return indicates that + * the actual value was not equal to the expected value. */ public final boolean compareAndSet(boolean expect, boolean update) { int e = expect ? 1 : 0; @@ -79,9 +76,13 @@ public class AtomicBoolean implements java.io.Serializable { } /** - * Atomically set the value to the given updated value - * if the current value <tt>==</tt> the expected value. - * May fail spuriously. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a> + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param expect the expected value * @param update the new value * @return true if successful. @@ -102,7 +103,7 @@ public class AtomicBoolean implements java.io.Serializable { } /** - * Sets to the given value and returns the previous value. + * Atomically sets to the given value and returns the previous value. * * @param newValue the new value * @return the previous value diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java index 2b9d15c..1aa32f4 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java @@ -8,20 +8,19 @@ package java.util.concurrent.atomic; import sun.misc.Unsafe; /** - * An <tt>int</tt> value that may be updated atomically. See the + * An {@code int} value that may be updated atomically. See the * {@link java.util.concurrent.atomic} package specification for * description of the properties of atomic variables. An - * <tt>AtomicInteger</tt> is used in applications such as atomically + * {@code AtomicInteger} is used in applications such as atomically * incremented counters, and cannot be used as a replacement for an * {@link java.lang.Integer}. However, this class does extend - * <tt>Number</tt> to allow uniform access by tools and utilities that + * {@code Number} to allow uniform access by tools and utilities that * deal with numerically-based classes. - * * * @since 1.5 * @author Doug Lea */ -public class AtomicInteger extends Number implements java.io.Serializable { +public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates @@ -34,13 +33,13 @@ public class AtomicInteger extends Number implements java.io.Serializable { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); - } catch(Exception ex) { throw new Error(ex); } + } catch (Exception ex) { throw new Error(ex); } } private volatile int value; /** - * Create a new AtomicInteger with the given initial value. + * Creates a new AtomicInteger with the given initial value. * * @param initialValue the initial value */ @@ -49,22 +48,22 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * Create a new AtomicInteger with initial value <tt>0</tt>. + * Creates a new AtomicInteger with initial value {@code 0}. */ public AtomicInteger() { } /** - * Get the current value. + * Gets the current value. * * @return the current value */ public final int get() { return value; } - + /** - * Set to the given value. + * Sets to the given value. * * @param newValue the new value */ @@ -73,7 +72,7 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * Set to the give value and return the old value. + * Atomically sets to the given value and returns the old value. * * @param newValue the new value * @return the previous value @@ -85,35 +84,39 @@ public class AtomicInteger extends Number implements java.io.Serializable { return current; } } - - + /** - * Atomically set the value to the given updated value - * if the current value <tt>==</tt> the expected value. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * * @param expect the expected value * @param update the new value * @return true if successful. False return indicates that * the actual value was not equal to the expected value. */ public final boolean compareAndSet(int expect, int update) { - return unsafe.compareAndSwapInt(this, valueOffset, expect, update); + return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } /** - * Atomically set the value to the given updated value - * if the current value <tt>==</tt> the expected value. - * May fail spuriously. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a> + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param expect the expected value * @param update the new value * @return true if successful. */ public final boolean weakCompareAndSet(int expect, int update) { - return unsafe.compareAndSwapInt(this, valueOffset, expect, update); + return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } - /** - * Atomically increment by one the current value. + * Atomically increments by one the current value. + * * @return the previous value */ public final int getAndIncrement() { @@ -124,10 +127,10 @@ public class AtomicInteger extends Number implements java.io.Serializable { return current; } } - - + /** - * Atomically decrement by one the current value. + * Atomically decrements by one the current value. + * * @return the previous value */ public final int getAndDecrement() { @@ -138,10 +141,10 @@ public class AtomicInteger extends Number implements java.io.Serializable { return current; } } - - + /** - * Atomically add the given value to current value. + * Atomically adds the given value to the current value. + * * @param delta the value to add * @return the previous value */ @@ -155,7 +158,8 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * Atomically increment by one the current value. + * Atomically increments by one the current value. + * * @return the updated value */ public final int incrementAndGet() { @@ -166,9 +170,10 @@ public class AtomicInteger extends Number implements java.io.Serializable { return next; } } - + /** - * Atomically decrement by one the current value. + * Atomically decrements by one the current value. + * * @return the updated value */ public final int decrementAndGet() { @@ -179,10 +184,10 @@ public class AtomicInteger extends Number implements java.io.Serializable { return next; } } - - + /** - * Atomically add the given value to current value. + * Atomically adds the given value to the current value. + * * @param delta the value to add * @return the updated value */ diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java index 95807f3..ee21b13 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java @@ -9,14 +9,14 @@ import sun.misc.Unsafe; import java.util.*; /** - * An <tt>int</tt> array in which elements may be updated atomically. + * An {@code int} array in which elements may be updated atomically. * See the {@link java.util.concurrent.atomic} package * specification for description of the properties of atomic * variables. * @since 1.5 * @author Doug Lea */ -public class AtomicIntegerArray implements java.io.Serializable { +public class AtomicIntegerArray implements java.io.Serializable { private static final long serialVersionUID = 2862133569453604235L; // setup to use Unsafe.compareAndSwapInt for updates @@ -34,7 +34,7 @@ public class AtomicIntegerArray implements java.io.Serializable { } /** - * Create a new AtomicIntegerArray of given length. + * Creates a new AtomicIntegerArray of given length. * * @param length the length of the array */ @@ -46,14 +46,14 @@ public class AtomicIntegerArray implements java.io.Serializable { } /** - * Create a new AtomicIntegerArray with the same length as, and + * Creates a new AtomicIntegerArray with the same length as, and * all elements copied from, the given array. * * @param array the array to copy elements from * @throws NullPointerException if array is null */ public AtomicIntegerArray(int[] array) { - if (array == null) + if (array == null) throw new NullPointerException(); int length = array.length; this.array = new int[length]; @@ -76,7 +76,7 @@ public class AtomicIntegerArray implements java.io.Serializable { } /** - * Get the current value at position <tt>i</tt>. + * Gets the current value at position {@code i}. * * @param i the index * @return the current value @@ -84,9 +84,9 @@ public class AtomicIntegerArray implements java.io.Serializable { public final int get(int i) { return unsafe.getIntVolatile(array, rawIndex(i)); } - + /** - * Set the element at position <tt>i</tt> to the given value. + * Sets the element at position {@code i} to the given value. * * @param i the index * @param newValue the new value @@ -94,10 +94,10 @@ public class AtomicIntegerArray implements java.io.Serializable { public final void set(int i, int newValue) { unsafe.putIntVolatile(array, rawIndex(i), newValue); } - + /** - * Set the element at position <tt>i</tt> to the given value and return the - * old value. + * Atomically sets the element at position {@code i} to the given + * value and returns the old value. * * @param i the index * @param newValue the new value @@ -110,10 +110,10 @@ public class AtomicIntegerArray implements java.io.Serializable { return current; } } - + /** - * Atomically set the value to the given updated value - * if the current value <tt>==</tt> the expected value. + * Atomically sets the element at position {@code i} to the given + * updated value if the current value {@code ==} the expected value. * * @param i the index * @param expect the expected value @@ -122,14 +122,17 @@ public class AtomicIntegerArray implements java.io.Serializable { * the actual value was not equal to the expected value. */ public final boolean compareAndSet(int i, int expect, int update) { - return unsafe.compareAndSwapInt(array, rawIndex(i), + return unsafe.compareAndSwapInt(array, rawIndex(i), expect, update); } /** - * Atomically set the value to the given updated value - * if the current value <tt>==</tt> the expected value. - * May fail spuriously. + * Atomically sets the element at position {@code i} to the given + * updated value if the current value {@code ==} the expected value. + * + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a> + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. * * @param i the index * @param expect the expected value @@ -141,10 +144,10 @@ public class AtomicIntegerArray implements java.io.Serializable { } /** - * Atomically increment by one the element at index <tt>i</tt>. + * Atomically increments by one the element at index {@code i}. * * @param i the index - * @return the previous value; + * @return the previous value */ public final int getAndIncrement(int i) { while (true) { @@ -154,12 +157,12 @@ public class AtomicIntegerArray implements java.io.Serializable { return current; } } - + /** - * Atomically decrement by one the element at index <tt>i</tt>. + * Atomically decrements by one the element at index {@code i}. * * @param i the index - * @return the previous value; + * @return the previous value */ public final int getAndDecrement(int i) { while (true) { @@ -169,13 +172,13 @@ public class AtomicIntegerArray implements java.io.Serializable { return current; } } - + /** - * Atomically add the given value to element at index <tt>i</tt>. + * Atomically adds the given value to the element at index {@code i}. * * @param i the index * @param delta the value to add - * @return the previous value; + * @return the previous value */ public final int getAndAdd(int i, int delta) { while (true) { @@ -187,10 +190,10 @@ public class AtomicIntegerArray implements java.io.Serializable { } /** - * Atomically increment by one the element at index <tt>i</tt>. + * Atomically increments by one the element at index {@code i}. * * @param i the index - * @return the updated value; + * @return the updated value */ public final int incrementAndGet(int i) { while (true) { @@ -200,12 +203,12 @@ public class AtomicIntegerArray implements java.io.Serializable { return next; } } - + /** - * Atomically decrement by one the element at index <tt>i</tt>. + * Atomically decrements by one the element at index {@code i}. * * @param i the index - * @return the updated value; + * @return the updated value */ public final int decrementAndGet(int i) { while (true) { @@ -215,13 +218,13 @@ public class AtomicIntegerArray implements java.io.Serializable { return next; } } - + /** - * Atomically add the given value to element at index <tt>i</tt>. + * Atomically adds the given value to the element at index {@code i}. * * @param i the index * @param delta the value to add - * @return the updated value; + * @return the updated value */ public final int addAndGet(int i, int delta) { while (true) { @@ -231,7 +234,7 @@ public class AtomicIntegerArray implements java.io.Serializable { return next; } } - + /** * Returns the String representation of the current values of array. * @return the String representation of the current values of array. diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java index 5f289c2..f8d2c81 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java @@ -8,35 +8,40 @@ package java.util.concurrent.atomic; import sun.misc.Unsafe; import java.lang.reflect.*; +import org.apache.harmony.kernel.vm.VM; +import dalvik.system.VMStack; + /** * A reflection-based utility that enables atomic updates to - * designated <tt>volatile int</tt> fields of designated classes. + * designated {@code volatile int} fields of designated classes. * This class is designed for use in atomic data structures in which * several fields of the same node are independently subject to atomic * updates. * - * <p> Note that the guarantees of the <tt>compareAndSet</tt> method - * in this class are weaker than in other atomic classes. Because this - * class cannot ensure that all uses of the field are appropriate for - * purposes of atomic access, it can guarantee atomicity and volatile - * semantics only with respect to other invocations of - * <tt>compareAndSet</tt> and <tt>set</tt>. + * <p>Note that the guarantees of the {@code compareAndSet} + * method in this class are weaker than in other atomic classes. + * Because this class cannot ensure that all uses of the field + * are appropriate for purposes of atomic access, it can + * guarantee atomicity only with respect to other invocations of + * {@code compareAndSet} and {@code set} on the same updater. + * * @since 1.5 * @author Doug Lea * @param <T> The type of the object holding the updatable field */ public abstract class AtomicIntegerFieldUpdater<T> { /** - * Creates an updater for objects with the given field. The Class - * argument is needed to check that reflective types and generic - * types match. + * Creates and returns an updater for objects with the given field. + * The Class argument is needed to check that reflective types and + * generic types match. + * * @param tclass the class of the objects holding the field - * @param fieldName the name of the field to be updated. + * @param fieldName the name of the field to be updated * @return the updater * @throws IllegalArgumentException if the field is not a - * volatile integer type. + * volatile integer type * @throws RuntimeException with a nested reflection-based - * exception if the class does not hold field or is the wrong type. + * exception if the class does not hold field or is the wrong type */ public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) { return new AtomicIntegerFieldUpdaterImpl<U>(tclass, fieldName); @@ -49,57 +54,63 @@ public abstract class AtomicIntegerFieldUpdater<T> { } /** - * Atomically set the value of the field of the given object managed - * by this Updater to the given updated value if the current value - * <tt>==</tt> the expected value. This method is guaranteed to be - * atomic with respect to other calls to <tt>compareAndSet</tt> and - * <tt>set</tt>, but not necessarily with respect to other - * changes in the field. + * Atomically sets the field of the given object managed by this updater + * to the given updated value if the current value {@code ==} the + * expected value. This method is guaranteed to be atomic with respect to + * other calls to {@code compareAndSet} and {@code set}, but not + * necessarily with respect to other changes in the field. + * * @param obj An object whose field to conditionally set * @param expect the expected value * @param update the new value - * @return true if successful. - * @throws ClassCastException if <tt>obj</tt> is not an instance - * of the class possessing the field established in the constructor. + * @return true if successful + * @throws ClassCastException if {@code obj} is not an instance + * of the class possessing the field established in the constructor */ - public abstract boolean compareAndSet(T obj, int expect, int update); /** - * Atomically set the value of the field of the given object managed - * by this Updater to the given updated value if the current value - * <tt>==</tt> the expected value. This method is guaranteed to be - * atomic with respect to other calls to <tt>compareAndSet</tt> and - * <tt>set</tt>, but not necessarily with respect to other - * changes in the field, and may fail spuriously. + * Atomically sets the field of the given object managed by this updater + * to the given updated value if the current value {@code ==} the + * expected value. This method is guaranteed to be atomic with respect to + * other calls to {@code compareAndSet} and {@code set}, but not + * necessarily with respect to other changes in the field. + * + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a> + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param obj An object whose field to conditionally set * @param expect the expected value * @param update the new value - * @return true if successful. - * @throws ClassCastException if <tt>obj</tt> is not an instance - * of the class possessing the field established in the constructor. + * @return true if successful + * @throws ClassCastException if {@code obj} is not an instance + * of the class possessing the field established in the constructor */ - public abstract boolean weakCompareAndSet(T obj, int expect, int update); /** - * Set the field of the given object managed by this updater. This - * operation is guaranteed to act as a volatile store with respect - * to subsequent invocations of <tt>compareAndSet</tt>. + * Sets the field of the given object managed by this updater to the + * given updated value. This operation is guaranteed to act as a volatile + * store with respect to subsequent invocations of {@code compareAndSet}. + * * @param obj An object whose field to set * @param newValue the new value */ public abstract void set(T obj, int newValue); /** - * Get the current value held in the field by the given object. + * Gets the current value held in the field of the given object managed + * by this updater. + * * @param obj An object whose field to get * @return the current value */ public abstract int get(T obj); /** - * Set to the given value and return the old value. + * Atomically sets the field of the given object managed by this updater + * to the given value and returns the old value. * * @param obj An object whose field to get and set * @param newValue the new value @@ -114,9 +125,11 @@ public abstract class AtomicIntegerFieldUpdater<T> { } /** - * Atomically increment by one the current value. + * Atomically increments by one the current value of the field of the + * given object managed by this updater. + * * @param obj An object whose field to get and set - * @return the previous value; + * @return the previous value */ public int getAndIncrement(T obj) { for (;;) { @@ -127,11 +140,12 @@ public abstract class AtomicIntegerFieldUpdater<T> { } } - /** - * Atomically decrement by one the current value. + * Atomically decrements by one the current value of the field of the + * given object managed by this updater. + * * @param obj An object whose field to get and set - * @return the previous value; + * @return the previous value */ public int getAndDecrement(T obj) { for (;;) { @@ -142,12 +156,13 @@ public abstract class AtomicIntegerFieldUpdater<T> { } } - /** - * Atomically add the given value to current value. + * Atomically adds the given value to the current value of the field of + * the given object managed by this updater. + * * @param obj An object whose field to get and set * @param delta the value to add - * @return the previous value; + * @return the previous value */ public int getAndAdd(T obj, int delta) { for (;;) { @@ -159,9 +174,11 @@ public abstract class AtomicIntegerFieldUpdater<T> { } /** - * Atomically increment by one the current value. + * Atomically increments by one the current value of the field of the + * given object managed by this updater. + * * @param obj An object whose field to get and set - * @return the updated value; + * @return the updated value */ public int incrementAndGet(T obj) { for (;;) { @@ -172,11 +189,12 @@ public abstract class AtomicIntegerFieldUpdater<T> { } } - /** - * Atomically decrement by one the current value. + * Atomically decrements by one the current value of the field of the + * given object managed by this updater. + * * @param obj An object whose field to get and set - * @return the updated value; + * @return the updated value */ public int decrementAndGet(T obj) { for (;;) { @@ -187,12 +205,13 @@ public abstract class AtomicIntegerFieldUpdater<T> { } } - /** - * Atomically add the given value to current value. + * Atomically adds the given value to the current value of the field of + * the given object managed by this updater. + * * @param obj An object whose field to get and set * @param delta the value to add - * @return the updated value; + * @return the updated value */ public int addAndGet(T obj, int delta) { for (;;) { @@ -212,49 +231,83 @@ public abstract class AtomicIntegerFieldUpdater<T> { // END android-changed private final long offset; private final Class<T> tclass; + private final Class cclass; AtomicIntegerFieldUpdaterImpl(Class<T> tclass, String fieldName) { Field field = null; + Class caller = null; + int modifiers = 0; try { field = tclass.getDeclaredField(fieldName); - } catch(Exception ex) { + // BEGIN android-changed + caller = VMStack.getStackClass2(); + // END android-changed + modifiers = field.getModifiers(); + + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + int type = Modifier.isPublic(modifiers) + ? Member.PUBLIC : Member.DECLARED; + smgr.checkMemberAccess(tclass, type); + smgr.checkPackageAccess(tclass.getPackage().getName()); + } + } catch (Exception ex) { throw new RuntimeException(ex); } - + Class fieldt = field.getType(); if (fieldt != int.class) throw new IllegalArgumentException("Must be integer type"); - - if (!Modifier.isVolatile(field.getModifiers())) + + if (!Modifier.isVolatile(modifiers)) throw new IllegalArgumentException("Must be volatile type"); - + + this.cclass = (Modifier.isProtected(modifiers) && + caller != tclass) ? caller : null; this.tclass = tclass; offset = unsafe.objectFieldOffset(field); } - public boolean compareAndSet(T obj, int expect, int update) { + private void fullCheck(T obj) { if (!tclass.isInstance(obj)) throw new ClassCastException(); + if (cclass != null) + ensureProtectedAccess(obj); + } + + public boolean compareAndSet(T obj, int expect, int update) { + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); return unsafe.compareAndSwapInt(obj, offset, expect, update); } public boolean weakCompareAndSet(T obj, int expect, int update) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); return unsafe.compareAndSwapInt(obj, offset, expect, update); } public void set(T obj, int newValue) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); unsafe.putIntVolatile(obj, offset, newValue); } public final int get(T obj) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); return unsafe.getIntVolatile(obj, offset); } + + private void ensureProtectedAccess(T obj) { + if (cclass.isInstance(obj)) { + return; + } + throw new RuntimeException( + new IllegalAccessException("Class " + + cclass.getName() + + " can not access a protected member of class " + + tclass.getName() + + " using an instance of " + + obj.getClass().getName() + ) + ); + } } } - diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java index 7f41364..9b56002 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java @@ -8,19 +8,19 @@ package java.util.concurrent.atomic; import sun.misc.Unsafe; /** - * A <tt>long</tt> value that may be updated atomically. See the + * A {@code long} value that may be updated atomically. See the * {@link java.util.concurrent.atomic} package specification for * description of the properties of atomic variables. An - * <tt>AtomicLong</tt> is used in applications such as atomically + * {@code AtomicLong} is used in applications such as atomically * incremented sequence numbers, and cannot be used as a replacement * for a {@link java.lang.Long}. However, this class does extend - * <tt>Number</tt> to allow uniform access by tools and utilities that + * {@code Number} to allow uniform access by tools and utilities that * deal with numerically-based classes. * * @since 1.5 * @author Doug Lea */ -public class AtomicLong extends Number implements java.io.Serializable { +public class AtomicLong extends Number implements java.io.Serializable { private static final long serialVersionUID = 1927816293512124184L; // setup to use Unsafe.compareAndSwapLong for updates @@ -30,12 +30,14 @@ public class AtomicLong extends Number implements java.io.Serializable { private static final long valueOffset; /** - * Record whether the underlying JVM supports lockless - * CompareAndSet for longs. While the unsafe.CompareAndSetLong + * Records whether the underlying JVM supports lockless + * compareAndSwap for longs. While the Unsafe.compareAndSwapLong * method works in either case, some constructions should be * handled at Java level to avoid locking user-visible locks. + * + * Initialised in the static block. */ - static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8(); + static final boolean VM_SUPPORTS_LONG_CAS; /** * Returns whether underlying JVM supports lockless CompareAndSet @@ -47,13 +49,22 @@ public class AtomicLong extends Number implements java.io.Serializable { try { valueOffset = unsafe.objectFieldOffset (AtomicLong.class.getDeclaredField("value")); - } catch(Exception ex) { throw new Error(ex); } + } catch (Exception ex) { throw new Error(ex); } + + boolean longCASSupport; + try { + longCASSupport = VMSupportsCS8(); + } catch (UnsatisfiedLinkError e) { + // assume there's support if the native isn't provided by the VM + longCASSupport = true; + } + VM_SUPPORTS_LONG_CAS = longCASSupport; } private volatile long value; /** - * Create a new AtomicLong with the given initial value. + * Creates a new AtomicLong with the given initial value. * * @param initialValue the initial value */ @@ -62,31 +73,31 @@ public class AtomicLong extends Number implements java.io.Serializable { } /** - * Create a new AtomicLong with initial value <tt>0</tt>. + * Creates a new AtomicLong with initial value {@code 0}. */ public AtomicLong() { } - + /** - * Get the current value. + * Gets the current value. * * @return the current value */ public final long get() { return value; } - + /** - * Set to the given value. + * Sets to the given value. * * @param newValue the new value */ public final void set(long newValue) { value = newValue; } - + /** - * Set to the give value and return the old value. + * Atomically sets to the given value and returns the old value. * * @param newValue the new value * @return the previous value @@ -98,33 +109,39 @@ public class AtomicLong extends Number implements java.io.Serializable { return current; } } - + /** - * Atomically set the value to the given updated value - * if the current value <tt>==</tt> the expected value. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * * @param expect the expected value * @param update the new value * @return true if successful. False return indicates that * the actual value was not equal to the expected value. */ public final boolean compareAndSet(long expect, long update) { - return unsafe.compareAndSwapLong(this, valueOffset, expect, update); + return unsafe.compareAndSwapLong(this, valueOffset, expect, update); } /** - * Atomically set the value to the given updated value - * if the current value <tt>==</tt> the expected value. - * May fail spuriously. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a> + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param expect the expected value * @param update the new value * @return true if successful. */ public final boolean weakCompareAndSet(long expect, long update) { - return unsafe.compareAndSwapLong(this, valueOffset, expect, update); + return unsafe.compareAndSwapLong(this, valueOffset, expect, update); } - + /** - * Atomically increment by one the current value. + * Atomically increments by one the current value. + * * @return the previous value */ public final long getAndIncrement() { @@ -135,10 +152,10 @@ public class AtomicLong extends Number implements java.io.Serializable { return current; } } - - + /** - * Atomically decrement by one the current value. + * Atomically decrements by one the current value. + * * @return the previous value */ public final long getAndDecrement() { @@ -149,10 +166,10 @@ public class AtomicLong extends Number implements java.io.Serializable { return current; } } - - + /** - * Atomically add the given value to current value. + * Atomically adds the given value to the current value. + * * @param delta the value to add * @return the previous value */ @@ -164,9 +181,10 @@ public class AtomicLong extends Number implements java.io.Serializable { return current; } } - + /** - * Atomically increment by one the current value. + * Atomically increments by one the current value. + * * @return the updated value */ public final long incrementAndGet() { @@ -177,9 +195,10 @@ public class AtomicLong extends Number implements java.io.Serializable { return next; } } - + /** - * Atomically decrement by one the current value. + * Atomically decrements by one the current value. + * * @return the updated value */ public final long decrementAndGet() { @@ -190,10 +209,10 @@ public class AtomicLong extends Number implements java.io.Serializable { return next; } } - - + /** - * Atomically add the given value to current value. + * Atomically adds the given value to the current value. + * * @param delta the value to add * @return the updated value */ @@ -205,7 +224,7 @@ public class AtomicLong extends Number implements java.io.Serializable { return next; } } - + /** * Returns the String representation of the current value. * @return the String representation of the current value. @@ -220,7 +239,7 @@ public class AtomicLong extends Number implements java.io.Serializable { } public long longValue() { - return (long)get(); + return get(); } public float floatValue() { @@ -230,5 +249,5 @@ public class AtomicLong extends Number implements java.io.Serializable { public double doubleValue() { return (double)get(); } - + } diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java index a0e76b4..d96f4c2 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java @@ -9,13 +9,13 @@ import sun.misc.Unsafe; import java.util.*; /** - * A <tt>long</tt> array in which elements may be updated atomically. + * A {@code long} array in which elements may be updated atomically. * See the {@link java.util.concurrent.atomic} package specification * for description of the properties of atomic variables. * @since 1.5 * @author Doug Lea */ -public class AtomicLongArray implements java.io.Serializable { +public class AtomicLongArray implements java.io.Serializable { private static final long serialVersionUID = -2308431214976778248L; // setup to use Unsafe.compareAndSwapInt for updates @@ -33,25 +33,26 @@ public class AtomicLongArray implements java.io.Serializable { } /** - * Create a new AtomicLongArray of given length. + * Creates a new AtomicLongArray of given length. + * * @param length the length of the array */ public AtomicLongArray(int length) { array = new long[length]; // must perform at least one volatile write to conform to JMM - if (length > 0) + if (length > 0) unsafe.putLongVolatile(array, rawIndex(0), 0); } /** - * Create a new AtomicLongArray with the same length as, and + * Creates a new AtomicLongArray with the same length as, and * all elements copied from, the given array. * * @param array the array to copy elements from * @throws NullPointerException if array is null */ public AtomicLongArray(long[] array) { - if (array == null) + if (array == null) throw new NullPointerException(); int length = array.length; this.array = new long[length]; @@ -74,7 +75,7 @@ public class AtomicLongArray implements java.io.Serializable { } /** - * Get the current value at position <tt>i</tt>. + * Gets the current value at position {@code i}. * * @param i the index * @return the current value @@ -82,9 +83,9 @@ public class AtomicLongArray implements java.io.Serializable { public final long get(int i) { return unsafe.getLongVolatile(array, rawIndex(i)); } - + /** - * Set the element at position <tt>i</tt> to the given value. + * Sets the element at position {@code i} to the given value. * * @param i the index * @param newValue the new value @@ -92,10 +93,10 @@ public class AtomicLongArray implements java.io.Serializable { public final void set(int i, long newValue) { unsafe.putLongVolatile(array, rawIndex(i), newValue); } - + /** - * Set the element at position <tt>i</tt> to the given value and return the - * old value. + * Atomically sets the element at position {@code i} to the given value + * and returns the old value. * * @param i the index * @param newValue the new value @@ -108,10 +109,11 @@ public class AtomicLongArray implements java.io.Serializable { return current; } } - + /** - * Atomically set the value to the given updated value - * if the current value <tt>==</tt> the expected value. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * * @param i the index * @param expect the expected value * @param update the new value @@ -119,14 +121,18 @@ public class AtomicLongArray implements java.io.Serializable { * the actual value was not equal to the expected value. */ public final boolean compareAndSet(int i, long expect, long update) { - return unsafe.compareAndSwapLong(array, rawIndex(i), + return unsafe.compareAndSwapLong(array, rawIndex(i), expect, update); } /** - * Atomically set the value to the given updated value - * if the current value <tt>==</tt> the expected value. - * May fail spuriously. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a> + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param i the index * @param expect the expected value * @param update the new value @@ -137,10 +143,10 @@ public class AtomicLongArray implements java.io.Serializable { } /** - * Atomically increment by one the element at index <tt>i</tt>. + * Atomically increments by one the element at index {@code i}. * * @param i the index - * @return the previous value; + * @return the previous value */ public final long getAndIncrement(int i) { while (true) { @@ -150,12 +156,12 @@ public class AtomicLongArray implements java.io.Serializable { return current; } } - + /** - * Atomically decrement by one the element at index <tt>i</tt>. + * Atomically decrements by one the element at index {@code i}. * * @param i the index - * @return the previous value; + * @return the previous value */ public final long getAndDecrement(int i) { while (true) { @@ -165,13 +171,13 @@ public class AtomicLongArray implements java.io.Serializable { return current; } } - + /** - * Atomically add the given value to element at index <tt>i</tt>. + * Atomically adds the given value to the element at index {@code i}. * * @param i the index * @param delta the value to add - * @return the previous value; + * @return the previous value */ public final long getAndAdd(int i, long delta) { while (true) { @@ -181,13 +187,12 @@ public class AtomicLongArray implements java.io.Serializable { return current; } } - /** - * Atomically increment the element at index <tt>i</tt>. + * Atomically increments by one the element at index {@code i}. * * @param i the index - * @return the updated value; + * @return the updated value */ public final long incrementAndGet(int i) { while (true) { @@ -197,12 +202,12 @@ public class AtomicLongArray implements java.io.Serializable { return next; } } - + /** - * Atomically decrement the element at index <tt>i</tt>. + * Atomically decrements by one the element at index {@code i}. * * @param i the index - * @return the updated value; + * @return the updated value */ public final long decrementAndGet(int i) { while (true) { @@ -212,13 +217,13 @@ public class AtomicLongArray implements java.io.Serializable { return next; } } - + /** - * Atomically add the given value to element at index <tt>i</tt>. + * Atomically adds the given value to the element at index {@code i}. * * @param i the index * @param delta the value to add - * @return the updated value; + * @return the updated value */ public long addAndGet(int i, long delta) { while (true) { @@ -238,5 +243,5 @@ public class AtomicLongArray implements java.io.Serializable { get(0); return Arrays.toString(array); } - + } diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java index dcb8b2e..d97c2e4 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java @@ -8,19 +8,22 @@ package java.util.concurrent.atomic; import sun.misc.Unsafe; import java.lang.reflect.*; +import org.apache.harmony.kernel.vm.VM; +import dalvik.system.VMStack; + /** * A reflection-based utility that enables atomic updates to - * designated <tt>volatile long</tt> fields of designated classes. + * designated {@code volatile long} fields of designated classes. * This class is designed for use in atomic data structures in which * several fields of the same node are independently subject to atomic * updates. * - * <p> Note that the guarantees of the <tt>compareAndSet</tt> method - * in this class are weaker than in other atomic classes. Because this - * class cannot ensure that all uses of the field are appropriate for - * purposes of atomic access, it can guarantee atomicity and volatile - * semantics only with respect to other invocations of - * <tt>compareAndSet</tt> and <tt>set</tt>. + * <p>Note that the guarantees of the {@code compareAndSet} + * method in this class are weaker than in other atomic classes. + * Because this class cannot ensure that all uses of the field + * are appropriate for purposes of atomic access, it can + * guarantee atomicity only with respect to other invocations of + * {@code compareAndSet} and {@code set} on the same updater. * * @since 1.5 * @author Doug Lea @@ -28,9 +31,10 @@ import java.lang.reflect.*; */ public abstract class AtomicLongFieldUpdater<T> { /** - * Creates an updater for objects with the given field. The Class - * argument is needed to check that reflective types and generic - * types match. + * Creates and returns an updater for objects with the given field. + * The Class argument is needed to check that reflective types and + * generic types match. + * * @param tclass the class of the objects holding the field * @param fieldName the name of the field to be updated. * @return the updater @@ -53,57 +57,63 @@ public abstract class AtomicLongFieldUpdater<T> { } /** - * Atomically set the value of the field of the given object managed - * by this Updater to the given updated value if the current value - * <tt>==</tt> the expected value. This method is guaranteed to be - * atomic with respect to other calls to <tt>compareAndSet</tt> and - * <tt>set</tt>, but not necessarily with respect to other - * changes in the field. + * Atomically sets the field of the given object managed by this updater + * to the given updated value if the current value {@code ==} the + * expected value. This method is guaranteed to be atomic with respect to + * other calls to {@code compareAndSet} and {@code set}, but not + * necessarily with respect to other changes in the field. + * * @param obj An object whose field to conditionally set * @param expect the expected value * @param update the new value * @return true if successful. - * @throws ClassCastException if <tt>obj</tt> is not an instance + * @throws ClassCastException if {@code obj} is not an instance * of the class possessing the field established in the constructor. */ - public abstract boolean compareAndSet(T obj, long expect, long update); /** - * Atomically set the value of the field of the given object managed - * by this Updater to the given updated value if the current value - * <tt>==</tt> the expected value. This method is guaranteed to be - * atomic with respect to other calls to <tt>compareAndSet</tt> and - * <tt>set</tt>, but not necessarily with respect to other - * changes in the field, and may fail spuriously. + * Atomically sets the field of the given object managed by this updater + * to the given updated value if the current value {@code ==} the + * expected value. This method is guaranteed to be atomic with respect to + * other calls to {@code compareAndSet} and {@code set}, but not + * necessarily with respect to other changes in the field. + * + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a> + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param obj An object whose field to conditionally set * @param expect the expected value * @param update the new value * @return true if successful. - * @throws ClassCastException if <tt>obj</tt> is not an instance + * @throws ClassCastException if {@code obj} is not an instance * of the class possessing the field established in the constructor. */ - public abstract boolean weakCompareAndSet(T obj, long expect, long update); /** - * Set the field of the given object managed by this updater. This - * operation is guaranteed to act as a volatile store with respect - * to subsequent invocations of <tt>compareAndSet</tt>. + * Sets the field of the given object managed by this updater to the + * given updated value. This operation is guaranteed to act as a volatile + * store with respect to subsequent invocations of {@code compareAndSet}. + * * @param obj An object whose field to set * @param newValue the new value */ public abstract void set(T obj, long newValue); /** - * Get the current value held in the field by the given object. + * Gets the current value held in the field of the given object managed + * by this updater. + * * @param obj An object whose field to get * @return the current value */ public abstract long get(T obj); /** - * Set to the given value and return the old value. + * Atomically sets the field of the given object managed by this updater + * to the given value and returns the old value. * * @param obj An object whose field to get and set * @param newValue the new value @@ -118,9 +128,11 @@ public abstract class AtomicLongFieldUpdater<T> { } /** - * Atomically increment by one the current value. + * Atomically increments by one the current value of the field of the + * given object managed by this updater. + * * @param obj An object whose field to get and set - * @return the previous value; + * @return the previous value */ public long getAndIncrement(T obj) { for (;;) { @@ -131,11 +143,12 @@ public abstract class AtomicLongFieldUpdater<T> { } } - /** - * Atomically decrement by one the current value. + * Atomically decrements by one the current value of the field of the + * given object managed by this updater. + * * @param obj An object whose field to get and set - * @return the previous value; + * @return the previous value */ public long getAndDecrement(T obj) { for (;;) { @@ -146,12 +159,13 @@ public abstract class AtomicLongFieldUpdater<T> { } } - /** - * Atomically add the given value to current value. + * Atomically adds the given value to the current value of the field of + * the given object managed by this updater. + * * @param obj An object whose field to get and set * @param delta the value to add - * @return the previous value; + * @return the previous value */ public long getAndAdd(T obj, long delta) { for (;;) { @@ -163,9 +177,11 @@ public abstract class AtomicLongFieldUpdater<T> { } /** - * Atomically increment by one the current value. + * Atomically increments by one the current value of the field of the + * given object managed by this updater. + * * @param obj An object whose field to get and set - * @return the updated value; + * @return the updated value */ public long incrementAndGet(T obj) { for (;;) { @@ -176,11 +192,12 @@ public abstract class AtomicLongFieldUpdater<T> { } } - /** - * Atomically decrement by one the current value. + * Atomically decrements by one the current value of the field of the + * given object managed by this updater. + * * @param obj An object whose field to get and set - * @return the updated value; + * @return the updated value */ public long decrementAndGet(T obj) { for (;;) { @@ -191,12 +208,13 @@ public abstract class AtomicLongFieldUpdater<T> { } } - /** - * Atomically add the given value to current value. + * Atomically adds the given value to the current value of the field of + * the given object managed by this updater. + * * @param obj An object whose field to get and set * @param delta the value to add - * @return the updated value; + * @return the updated value */ public long addAndGet(T obj, long delta) { for (;;) { @@ -213,49 +231,83 @@ public abstract class AtomicLongFieldUpdater<T> { // END android-changed private final long offset; private final Class<T> tclass; + private final Class cclass; CASUpdater(Class<T> tclass, String fieldName) { Field field = null; + Class caller = null; + int modifiers = 0; try { field = tclass.getDeclaredField(fieldName); - } catch(Exception ex) { + // BEGIN android-changed + caller = VMStack.getStackClass2(); + // END android-changed + modifiers = field.getModifiers(); + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + int type = Modifier.isPublic(modifiers) + ? Member.PUBLIC : Member.DECLARED; + smgr.checkMemberAccess(tclass, type); + smgr.checkPackageAccess(tclass.getPackage().getName()); + } + } catch (Exception ex) { throw new RuntimeException(ex); } - + Class fieldt = field.getType(); if (fieldt != long.class) throw new IllegalArgumentException("Must be long type"); - - if (!Modifier.isVolatile(field.getModifiers())) + + if (!Modifier.isVolatile(modifiers)) throw new IllegalArgumentException("Must be volatile type"); - + + this.cclass = (Modifier.isProtected(modifiers) && + caller != tclass) ? caller : null; this.tclass = tclass; offset = unsafe.objectFieldOffset(field); } - public boolean compareAndSet(T obj, long expect, long update) { + private void fullCheck(T obj) { if (!tclass.isInstance(obj)) throw new ClassCastException(); + if (cclass != null) + ensureProtectedAccess(obj); + } + + public boolean compareAndSet(T obj, long expect, long update) { + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); return unsafe.compareAndSwapLong(obj, offset, expect, update); } public boolean weakCompareAndSet(T obj, long expect, long update) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); return unsafe.compareAndSwapLong(obj, offset, expect, update); } public void set(T obj, long newValue) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); unsafe.putLongVolatile(obj, offset, newValue); } public long get(T obj) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); return unsafe.getLongVolatile(obj, offset); } + + private void ensureProtectedAccess(T obj) { + if (cclass.isInstance(obj)) { + return; + } + throw new RuntimeException ( + new IllegalAccessException("Class " + + cclass.getName() + + " can not access a protected member of class " + + tclass.getName() + + " using an instance of " + + obj.getClass().getName() + ) + ); + } } @@ -265,32 +317,54 @@ public abstract class AtomicLongFieldUpdater<T> { // END android-changed private final long offset; private final Class<T> tclass; + private final Class cclass; LockedUpdater(Class<T> tclass, String fieldName) { Field field = null; + Class caller = null; + int modifiers = 0; try { field = tclass.getDeclaredField(fieldName); - } catch(Exception ex) { + // BEGIN android-changed + caller = VMStack.getStackClass2(); + // END android-changed + modifiers = field.getModifiers(); + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + int type = Modifier.isPublic(modifiers) + ? Member.PUBLIC : Member.DECLARED; + smgr.checkMemberAccess(tclass, type); + smgr.checkPackageAccess(tclass.getPackage().getName()); + } + } catch (Exception ex) { throw new RuntimeException(ex); } - + Class fieldt = field.getType(); if (fieldt != long.class) throw new IllegalArgumentException("Must be long type"); - - if (!Modifier.isVolatile(field.getModifiers())) + + if (!Modifier.isVolatile(modifiers)) throw new IllegalArgumentException("Must be volatile type"); - + + this.cclass = (Modifier.isProtected(modifiers) && + caller != tclass) ? caller : null; this.tclass = tclass; offset = unsafe.objectFieldOffset(field); } - public boolean compareAndSet(T obj, long expect, long update) { + private void fullCheck(T obj) { if (!tclass.isInstance(obj)) throw new ClassCastException(); + if (cclass != null) + ensureProtectedAccess(obj); + } + + public boolean compareAndSet(T obj, long expect, long update) { + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); synchronized(this) { long v = unsafe.getLong(obj, offset); - if (v != expect) + if (v != expect) return false; unsafe.putLong(obj, offset, update); return true; @@ -302,20 +376,36 @@ public abstract class AtomicLongFieldUpdater<T> { } public void set(T obj, long newValue) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); synchronized(this) { unsafe.putLong(obj, offset, newValue); } } + public void lazySet(T obj, long newValue) { + set(obj, newValue); + } + public long get(T obj) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); synchronized(this) { return unsafe.getLong(obj, offset); } } + + private void ensureProtectedAccess(T obj) { + if (cclass.isInstance(obj)) { + return; + } + throw new RuntimeException ( + new IllegalAccessException("Class " + + cclass.getName() + + " can not access a protected member of class " + + tclass.getName() + + " using an instance of " + + obj.getClass().getName() + ) + ); + } } } - diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java index 11c91ba..4877dc4 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java @@ -7,7 +7,7 @@ package java.util.concurrent.atomic; /** - * An <tt>AtomicMarkableReference</tt> maintains an object reference + * An {@code AtomicMarkableReference} maintains an object reference * along with a mark bit, that can be updated atomically. * <p> * <p> Implementation note. This implementation maintains markable @@ -31,7 +31,7 @@ public class AtomicMarkableReference<V> { private final AtomicReference<ReferenceBooleanPair<V>> atomicRef; /** - * Creates a new <tt>AtomicMarkableReference</tt> with the given + * Creates a new {@code AtomicMarkableReference} with the given * initial values. * * @param initialRef the initial reference @@ -61,10 +61,10 @@ public class AtomicMarkableReference<V> { /** * Returns the current values of both the reference and the mark. - * Typical usage is <tt>boolean[1] holder; ref = v.get(holder); </tt>. + * Typical usage is {@code boolean[1] holder; ref = v.get(holder); }. * * @param markHolder an array of size of at least one. On return, - * <tt>markholder[0]</tt> will hold the value of the mark. + * {@code markholder[0]} will hold the value of the mark. * @return the current value of the reference */ public V get(boolean[] markHolder) { @@ -76,12 +76,12 @@ public class AtomicMarkableReference<V> { /** * Atomically sets the value of both the reference and mark * to the given update values if the - * current reference is <tt>==</tt> to the expected reference - * and the current mark is equal to the expected mark. Any given - * invocation of this operation may fail (return - * <tt>false</tt>) spuriously, but repeated invocation when - * the current value holds the expected value and no other thread - * is also attempting to set the value will eventually succeed. + * current reference is {@code ==} to the expected reference + * and the current mark is equal to the expected mark. + * + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a> + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. * * @param expectedReference the expected value of the reference * @param newReference the new value for the reference @@ -93,7 +93,7 @@ public class AtomicMarkableReference<V> { V newReference, boolean expectedMark, boolean newMark) { - ReferenceBooleanPair current = atomicRef.get(); + ReferenceBooleanPair<V> current = atomicRef.get(); return expectedReference == current.reference && expectedMark == current.bit && ((newReference == current.reference && newMark == current.bit) || @@ -105,8 +105,8 @@ public class AtomicMarkableReference<V> { /** * Atomically sets the value of both the reference and mark * to the given update values if the - * current reference is <tt>==</tt> to the expected reference - * and the current mark is equal to the expected mark. + * current reference is {@code ==} to the expected reference + * and the current mark is equal to the expected mark. * * @param expectedReference the expected value of the reference * @param newReference the new value for the reference @@ -118,7 +118,7 @@ public class AtomicMarkableReference<V> { V newReference, boolean expectedMark, boolean newMark) { - ReferenceBooleanPair current = atomicRef.get(); + ReferenceBooleanPair<V> current = atomicRef.get(); return expectedReference == current.reference && expectedMark == current.bit && ((newReference == current.reference && newMark == current.bit) || @@ -134,16 +134,16 @@ public class AtomicMarkableReference<V> { * @param newMark the new value for the mark */ public void set(V newReference, boolean newMark) { - ReferenceBooleanPair current = atomicRef.get(); + ReferenceBooleanPair<V> current = atomicRef.get(); if (newReference != current.reference || newMark != current.bit) atomicRef.set(new ReferenceBooleanPair<V>(newReference, newMark)); } /** * Atomically sets the value of the mark to the given update value - * if the current reference is <tt>==</tt> to the expected + * if the current reference is {@code ==} to the expected * reference. Any given invocation of this operation may fail - * (return <tt>false</tt>) spuriously, but repeated invocation + * (return {@code false}) spuriously, but repeated invocation * when the current value holds the expected value and no other * thread is also attempting to set the value will eventually * succeed. @@ -153,7 +153,7 @@ public class AtomicMarkableReference<V> { * @return true if successful */ public boolean attemptMark(V expectedReference, boolean newMark) { - ReferenceBooleanPair current = atomicRef.get(); + ReferenceBooleanPair<V> current = atomicRef.get(); return expectedReference == current.reference && (newMark == current.bit || atomicRef.compareAndSet diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java index 89c050c..1de9b1c 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java @@ -15,7 +15,7 @@ import sun.misc.Unsafe; * @author Doug Lea * @param <V> The type of object referred to by this reference */ -public class AtomicReference<V> implements java.io.Serializable { +public class AtomicReference<V> implements java.io.Serializable { private static final long serialVersionUID = -1848883965231344442L; // BEGIN android-changed @@ -27,13 +27,13 @@ public class AtomicReference<V> implements java.io.Serializable { try { valueOffset = unsafe.objectFieldOffset (AtomicReference.class.getDeclaredField("value")); - } catch(Exception ex) { throw new Error(ex); } + } catch (Exception ex) { throw new Error(ex); } } private volatile V value; /** - * Create a new AtomicReference with the given initial value. + * Creates a new AtomicReference with the given initial value. * * @param initialValue the initial value */ @@ -42,55 +42,59 @@ public class AtomicReference<V> implements java.io.Serializable { } /** - * Create a new AtomicReference with null initial value. + * Creates a new AtomicReference with null initial value. */ public AtomicReference() { } - + /** - * Get the current value. + * Gets the current value. * * @return the current value */ public final V get() { return value; } - + /** - * Set to the given value. + * Sets to the given value. * * @param newValue the new value */ public final void set(V newValue) { value = newValue; } - + /** - * Atomically set the value to the given updated value - * if the current value <tt>==</tt> the expected value. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. * @param expect the expected value * @param update the new value * @return true if successful. False return indicates that * the actual value was not equal to the expected value. */ public final boolean compareAndSet(V expect, V update) { - return unsafe.compareAndSwapObject(this, valueOffset, expect, update); + return unsafe.compareAndSwapObject(this, valueOffset, expect, update); } /** - * Atomically set the value to the given updated value - * if the current value <tt>==</tt> the expected value. - * May fail spuriously. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a> + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param expect the expected value * @param update the new value * @return true if successful. */ public final boolean weakCompareAndSet(V expect, V update) { - return unsafe.compareAndSwapObject(this, valueOffset, expect, update); + return unsafe.compareAndSwapObject(this, valueOffset, expect, update); } /** - * Set to the given value and return the old value. + * Atomically sets to the given value and returns the old value. * * @param newValue the new value * @return the previous value diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java index 9ad800c..9a484e6 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java @@ -17,7 +17,7 @@ import java.util.*; * @author Doug Lea * @param <E> The base class of elements held in this array */ -public class AtomicReferenceArray<E> implements java.io.Serializable { +public class AtomicReferenceArray<E> implements java.io.Serializable { private static final long serialVersionUID = -6209656149925076980L; // BEGIN android-changed @@ -34,25 +34,25 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { } /** - * Create a new AtomicReferenceArray of given length. + * Creates a new AtomicReferenceArray of given length. * @param length the length of the array */ public AtomicReferenceArray(int length) { array = new Object[length]; // must perform at least one volatile write to conform to JMM - if (length > 0) + if (length > 0) unsafe.putObjectVolatile(array, rawIndex(0), null); } /** - * Create a new AtomicReferenceArray with the same length as, and + * Creates a new AtomicReferenceArray with the same length as, and * all elements copied from, the given array. * * @param array the array to copy elements from * @throws NullPointerException if array is null */ public AtomicReferenceArray(E[] array) { - if (array == null) + if (array == null) throw new NullPointerException(); int length = array.length; this.array = new Object[length]; @@ -76,7 +76,7 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { } /** - * Get the current value at position <tt>i</tt>. + * Gets the current value at position {@code i}. * * @param i the index * @return the current value @@ -84,9 +84,9 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { public final E get(int i) { return (E) unsafe.getObjectVolatile(array, rawIndex(i)); } - + /** - * Set the element at position <tt>i</tt> to the given value. + * Sets the element at position {@code i} to the given value. * * @param i the index * @param newValue the new value @@ -94,10 +94,10 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { public final void set(int i, E newValue) { unsafe.putObjectVolatile(array, rawIndex(i), newValue); } - + /** - * Set the element at position <tt>i</tt> to the given value and return the - * old value. + * Atomically sets the element at position {@code i} to the given + * value and returns the old value. * * @param i the index * @param newValue the new value @@ -110,10 +110,10 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { return current; } } - + /** - * Atomically set the value to the given updated value - * if the current value <tt>==</tt> the expected value. + * Atomically sets the element at position {@code i} to the given + * updated value if the current value {@code ==} the expected value. * @param i the index * @param expect the expected value * @param update the new value @@ -121,14 +121,18 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { * the actual value was not equal to the expected value. */ public final boolean compareAndSet(int i, E expect, E update) { - return unsafe.compareAndSwapObject(array, rawIndex(i), + return unsafe.compareAndSwapObject(array, rawIndex(i), expect, update); } /** - * Atomically set the value to the given updated value - * if the current value <tt>==</tt> the expected value. - * May fail spuriously. + * Atomically sets the element at position {@code i} to the given + * updated value if the current value {@code ==} the expected value. + * + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a> + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param i the index * @param expect the expected value * @param update the new value diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java index f20661d..fae134f 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java @@ -8,9 +8,12 @@ package java.util.concurrent.atomic; import sun.misc.Unsafe; import java.lang.reflect.*; +import org.apache.harmony.kernel.vm.VM; +import dalvik.system.VMStack; + /** * A reflection-based utility that enables atomic updates to - * designated <tt>volatile</tt> reference fields of designated + * designated {@code volatile} reference fields of designated * classes. This class is designed for use in atomic data structures * in which several reference fields of the same node are * independently subject to atomic updates. For example, a tree node @@ -33,12 +36,13 @@ import java.lang.reflect.*; * } * </pre> * - * <p> Note that the guarantees of the <tt>compareAndSet</tt> - * method in this class are weaker than in other atomic classes. Because this - * class cannot ensure that all uses of the field are appropriate for - * purposes of atomic access, it can guarantee atomicity and volatile - * semantics only with respect to other invocations of - * <tt>compareAndSet</tt> and <tt>set</tt>. + * <p>Note that the guarantees of the {@code compareAndSet} + * method in this class are weaker than in other atomic classes. + * Because this class cannot ensure that all uses of the field + * are appropriate for purposes of atomic access, it can + * guarantee atomicity only with respect to other invocations of + * {@code compareAndSet} and {@code set} on the same updater. + * * @since 1.5 * @author Doug Lea * @param <T> The type of the object holding the updatable field @@ -47,9 +51,10 @@ import java.lang.reflect.*; public abstract class AtomicReferenceFieldUpdater<T, V> { /** - * Creates an updater for objects with the given field. The Class - * arguments are needed to check that reflective types and generic - * types match. + * Creates and returns an updater for objects with the given field. + * The Class arguments are needed to check that reflective types and + * generic types match. + * * @param tclass the class of the objects holding the field. * @param vclass the class of the field * @param fieldName the name of the field to be updated. @@ -59,9 +64,8 @@ public abstract class AtomicReferenceFieldUpdater<T, V> { * exception if the class does not hold field or is the wrong type. */ public static <U, W> AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U> tclass, Class<W> vclass, String fieldName) { - // Currently rely on standard intrinsics implementation - return new AtomicReferenceFieldUpdaterImpl<U,W>(tclass, - vclass, + return new AtomicReferenceFieldUpdaterImpl<U,W>(tclass, + vclass, fieldName); } @@ -72,27 +76,30 @@ public abstract class AtomicReferenceFieldUpdater<T, V> { } /** - * Atomically set the value of the field of the given object managed - * by this Updater to the given updated value if the current value - * <tt>==</tt> the expected value. This method is guaranteed to be - * atomic with respect to other calls to <tt>compareAndSet</tt> and - * <tt>set</tt>, but not necessarily with respect to other - * changes in the field. + * Atomically sets the field of the given object managed by this updater + * to the given updated value if the current value {@code ==} the + * expected value. This method is guaranteed to be atomic with respect to + * other calls to {@code compareAndSet} and {@code set}, but not + * necessarily with respect to other changes in the field. + * * @param obj An object whose field to conditionally set * @param expect the expected value * @param update the new value * @return true if successful. */ - public abstract boolean compareAndSet(T obj, V expect, V update); /** - * Atomically set the value of the field of the given object managed - * by this Updater to the given updated value if the current value - * <tt>==</tt> the expected value. This method is guaranteed to be - * atomic with respect to other calls to <tt>compareAndSet</tt> and - * <tt>set</tt>, but not necessarily with respect to other - * changes in the field, and may fail spuriously. + * Atomically sets the field of the given object managed by this updater + * to the given updated value if the current value {@code ==} the + * expected value. This method is guaranteed to be atomic with respect to + * other calls to {@code compareAndSet} and {@code set}, but not + * necessarily with respect to other changes in the field. + * + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a> + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param obj An object whose field to conditionally set * @param expect the expected value * @param update the new value @@ -101,23 +108,27 @@ public abstract class AtomicReferenceFieldUpdater<T, V> { public abstract boolean weakCompareAndSet(T obj, V expect, V update); /** - * Set the field of the given object managed by this updater. This - * operation is guaranteed to act as a volatile store with respect - * to subsequent invocations of <tt>compareAndSet</tt>. + * Sets the field of the given object managed by this updater to the + * given updated value. This operation is guaranteed to act as a volatile + * store with respect to subsequent invocations of {@code compareAndSet}. + * * @param obj An object whose field to set * @param newValue the new value */ public abstract void set(T obj, V newValue); /** - * Get the current value held in the field by the given object. + * Gets the current value held in the field of the given object managed + * by this updater. + * * @param obj An object whose field to get * @return the current value */ public abstract V get(T obj); /** - * Set to the given value and return the old value. + * Atomically sets the field of the given object managed by this updater + * to the given value and returns the old value. * * @param obj An object whose field to get and set * @param newValue the new value @@ -131,67 +142,128 @@ public abstract class AtomicReferenceFieldUpdater<T, V> { } } - /** - * Standard hotspot implementation using intrinsics - */ - private static class AtomicReferenceFieldUpdaterImpl<T,V> extends AtomicReferenceFieldUpdater<T,V> { + private static final class AtomicReferenceFieldUpdaterImpl<T,V> + extends AtomicReferenceFieldUpdater<T,V> { // BEGIN android-changed private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // END android-changed private final long offset; private final Class<T> tclass; private final Class<V> vclass; + private final Class cclass; - AtomicReferenceFieldUpdaterImpl(Class<T> tclass, Class<V> vclass, String fieldName) { + /* + * Internal type checks within all update methods contain + * internal inlined optimizations checking for the common + * cases where the class is final (in which case a simple + * getClass comparison suffices) or is of type Object (in + * which case no check is needed because all objects are + * instances of Object). The Object case is handled simply by + * setting vclass to null in constructor. The targetCheck and + * updateCheck methods are invoked when these faster + * screenings fail. + */ + + AtomicReferenceFieldUpdaterImpl(Class<T> tclass, + Class<V> vclass, + String fieldName) { Field field = null; Class fieldClass = null; + Class caller = null; + int modifiers = 0; try { field = tclass.getDeclaredField(fieldName); + // BEGIN android-changed + caller = VMStack.getStackClass2(); + // END android-changed + modifiers = field.getModifiers(); + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + int type = Modifier.isPublic(modifiers) + ? Member.PUBLIC : Member.DECLARED; + smgr.checkMemberAccess(tclass, type); + smgr.checkPackageAccess(tclass.getPackage().getName()); + } fieldClass = field.getType(); - } catch(Exception ex) { + } catch (Exception ex) { throw new RuntimeException(ex); } - + if (vclass != fieldClass) throw new ClassCastException(); - - if (!Modifier.isVolatile(field.getModifiers())) + + if (!Modifier.isVolatile(modifiers)) throw new IllegalArgumentException("Must be volatile type"); + this.cclass = (Modifier.isProtected(modifiers) && + caller != tclass) ? caller : null; this.tclass = tclass; - this.vclass = vclass; + if (vclass == Object.class) + this.vclass = null; + else + this.vclass = vclass; offset = unsafe.objectFieldOffset(field); } - - public boolean compareAndSet(T obj, V expect, V update) { + void targetCheck(T obj) { + if (!tclass.isInstance(obj)) + throw new ClassCastException(); + if (cclass != null) + ensureProtectedAccess(obj); + } + + void updateCheck(T obj, V update) { if (!tclass.isInstance(obj) || - (update != null && !vclass.isInstance(update))) + (update != null && vclass != null && !vclass.isInstance(update))) throw new ClassCastException(); + if (cclass != null) + ensureProtectedAccess(obj); + } + + public boolean compareAndSet(T obj, V expect, V update) { + if (obj == null || obj.getClass() != tclass || cclass != null || + (update != null && vclass != null && + vclass != update.getClass())) + updateCheck(obj, update); return unsafe.compareAndSwapObject(obj, offset, expect, update); } public boolean weakCompareAndSet(T obj, V expect, V update) { // same implementation as strong form for now - if (!tclass.isInstance(obj) || - (update != null && !vclass.isInstance(update))) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null || + (update != null && vclass != null && + vclass != update.getClass())) + updateCheck(obj, update); return unsafe.compareAndSwapObject(obj, offset, expect, update); } - public void set(T obj, V newValue) { - if (!tclass.isInstance(obj) || - (newValue != null && !vclass.isInstance(newValue))) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null || + (newValue != null && vclass != null && + vclass != newValue.getClass())) + updateCheck(obj, newValue); unsafe.putObjectVolatile(obj, offset, newValue); } public V get(T obj) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) + targetCheck(obj); return (V)unsafe.getObjectVolatile(obj, offset); } + + private void ensureProtectedAccess(T obj) { + if (cclass.isInstance(obj)) { + return; + } + throw new RuntimeException ( + new IllegalAccessException("Class " + + cclass.getName() + + " can not access a protected member of class " + + tclass.getName() + + " using an instance of " + + obj.getClass().getName() + ) + ); + } } } - diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java index b0a02c2..a1d535f 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java @@ -7,8 +7,8 @@ package java.util.concurrent.atomic; /** - * An <tt>AtomicStampedReference</tt> maintains an object reference - * along with an integer "stamp", that can be updated atomically. + * An {@code AtomicStampedReference} maintains an object reference + * along with an integer "stamp", that can be updated atomically. * * <p> Implementation note. This implementation maintains stamped * references by creating internal objects representing "boxed" @@ -31,7 +31,7 @@ public class AtomicStampedReference<V> { private final AtomicReference<ReferenceIntegerPair<V>> atomicRef; /** - * Creates a new <tt>AtomicStampedReference</tt> with the given + * Creates a new {@code AtomicStampedReference} with the given * initial values. * * @param initialRef the initial reference @@ -62,10 +62,10 @@ public class AtomicStampedReference<V> { /** * Returns the current values of both the reference and the stamp. - * Typical usage is <tt>int[1] holder; ref = v.get(holder); </tt>. + * Typical usage is {@code int[1] holder; ref = v.get(holder); }. * * @param stampHolder an array of size of at least one. On return, - * <tt>stampholder[0]</tt> will hold the value of the stamp. + * {@code stampholder[0]} will hold the value of the stamp. * @return the current value of the reference */ public V get(int[] stampHolder) { @@ -77,12 +77,12 @@ public class AtomicStampedReference<V> { /** * Atomically sets the value of both the reference and stamp * to the given update values if the - * current reference is <tt>==</tt> to the expected reference - * and the current stamp is equal to the expected stamp. Any given - * invocation of this operation may fail (return - * <tt>false</tt>) spuriously, but repeated invocation when - * the current value holds the expected value and no other thread - * is also attempting to set the value will eventually succeed. + * current reference is {@code ==} to the expected reference + * and the current stamp is equal to the expected stamp. + * + * <p>May <a href="package-summary.html#Spurious">fail spuriously</a> + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. * * @param expectedReference the expected value of the reference * @param newReference the new value for the reference @@ -94,7 +94,7 @@ public class AtomicStampedReference<V> { V newReference, int expectedStamp, int newStamp) { - ReferenceIntegerPair current = atomicRef.get(); + ReferenceIntegerPair<V> current = atomicRef.get(); return expectedReference == current.reference && expectedStamp == current.integer && ((newReference == current.reference && @@ -107,8 +107,8 @@ public class AtomicStampedReference<V> { /** * Atomically sets the value of both the reference and stamp * to the given update values if the - * current reference is <tt>==</tt> to the expected reference - * and the current stamp is equal to the expected stamp. + * current reference is {@code ==} to the expected reference + * and the current stamp is equal to the expected stamp. * * @param expectedReference the expected value of the reference * @param newReference the new value for the reference @@ -120,7 +120,7 @@ public class AtomicStampedReference<V> { V newReference, int expectedStamp, int newStamp) { - ReferenceIntegerPair current = atomicRef.get(); + ReferenceIntegerPair<V> current = atomicRef.get(); return expectedReference == current.reference && expectedStamp == current.integer && ((newReference == current.reference && @@ -138,16 +138,16 @@ public class AtomicStampedReference<V> { * @param newStamp the new value for the stamp */ public void set(V newReference, int newStamp) { - ReferenceIntegerPair current = atomicRef.get(); + ReferenceIntegerPair<V> current = atomicRef.get(); if (newReference != current.reference || newStamp != current.integer) atomicRef.set(new ReferenceIntegerPair<V>(newReference, newStamp)); } /** * Atomically sets the value of the stamp to the given update value - * if the current reference is <tt>==</tt> to the expected + * if the current reference is {@code ==} to the expected * reference. Any given invocation of this operation may fail - * (return <tt>false</tt>) spuriously, but repeated invocation + * (return {@code false}) spuriously, but repeated invocation * when the current value holds the expected value and no other * thread is also attempting to set the value will eventually * succeed. @@ -157,7 +157,7 @@ public class AtomicStampedReference<V> { * @return true if successful */ public boolean attemptStamp(V expectedReference, int newStamp) { - ReferenceIntegerPair current = atomicRef.get(); + ReferenceIntegerPair<V> current = atomicRef.get(); return expectedReference == current.reference && (newStamp == current.integer || atomicRef.compareAndSet(current, diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/package-info.java b/concurrent/src/main/java/java/util/concurrent/atomic/package-info.java new file mode 100644 index 0000000..2252e5c --- /dev/null +++ b/concurrent/src/main/java/java/util/concurrent/atomic/package-info.java @@ -0,0 +1,163 @@ +/* + * 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 + */ + +/** + * A small toolkit of classes that support lock-free thread-safe + * programming on single variables. In essence, the classes in this + * package extend the notion of {@code volatile} values, fields, and + * array elements to those that also provide an atomic conditional update + * operation of the form: + * + * <pre> + * boolean compareAndSet(expectedValue, updateValue); + * </pre> + * + * <p>This method (which varies in argument types across different + * classes) atomically sets a variable to the {@code updateValue} if it + * currently holds the {@code expectedValue}, reporting {@code true} on + * success. The classes in this package also contain methods to get and + * unconditionally set values, as well as a weaker conditional atomic + * update operation {@code weakCompareAndSet} described below. + * + * <p>The specifications of these methods enable implementations to + * employ efficient machine-level atomic instructions that are available + * on contemporary processors. However on some platforms, support may + * entail some form of internal locking. Thus the methods are not + * strictly guaranteed to be non-blocking -- + * a thread may block transiently before performing the operation. + * + * <p>Instances of classes + * {@link java.util.concurrent.atomic.AtomicBoolean}, + * {@link java.util.concurrent.atomic.AtomicInteger}, + * {@link java.util.concurrent.atomic.AtomicLong}, and + * {@link java.util.concurrent.atomic.AtomicReference} + * each provide access and updates to a single variable of the + * corresponding type. Each class also provides appropriate utility + * methods for that type. For example, classes {@code AtomicLong} and + * {@code AtomicInteger} provide atomic increment methods. One + * application is to generate sequence numbers, as in: + * + * <pre> + * class Sequencer { + * private final AtomicLong sequenceNumber + * = new AtomicLong(0); + * public long next() { + * return sequenceNumber.getAndIncrement(); + * } + * } + * </pre> + * + * <p>The memory effects for accesses and updates of atomics generally + * follow the rules for volatiles, as stated in + * <a href="http://java.sun.com/docs/books/jls/"> The Java Language + * Specification, Third Edition (17.4 Memory Model)</a>: + * + * <ul> + * + * <li> {@code get} has the memory effects of reading a + * {@code volatile} variable. + * + * <li> {@code set} has the memory effects of writing (assigning) a + * {@code volatile} variable. + * + * <li>{@code weakCompareAndSet} atomically reads and conditionally + * writes a variable but does <em>not</em> + * create any happens-before orderings, so provides no guarantees + * with respect to previous or subsequent reads and writes of any + * variables other than the target of the {@code weakCompareAndSet}. + * + * <li> {@code compareAndSet} + * and all other read-and-update operations such as {@code getAndIncrement} + * have the memory effects of both reading and + * writing {@code volatile} variables. + * </ul> + * + * <p>In addition to classes representing single values, this package + * contains <em>Updater</em> classes that can be used to obtain + * {@code compareAndSet} operations on any selected {@code volatile} + * field of any selected class. + * + * {@link java.util.concurrent.atomic.AtomicReferenceFieldUpdater}, + * {@link java.util.concurrent.atomic.AtomicIntegerFieldUpdater}, and + * {@link java.util.concurrent.atomic.AtomicLongFieldUpdater} are + * reflection-based utilities that provide access to the associated + * field types. These are mainly of use in atomic data structures in + * which several {@code volatile} fields of the same node (for + * example, the links of a tree node) are independently subject to + * atomic updates. These classes enable greater flexibility in how + * and when to use atomic updates, at the expense of more awkward + * reflection-based setup, less convenient usage, and weaker + * guarantees. + * + * <p>The + * {@link java.util.concurrent.atomic.AtomicIntegerArray}, + * {@link java.util.concurrent.atomic.AtomicLongArray}, and + * {@link java.util.concurrent.atomic.AtomicReferenceArray} classes + * further extend atomic operation support to arrays of these types. + * These classes are also notable in providing {@code volatile} access + * semantics for their array elements, which is not supported for + * ordinary arrays. + * + * <a name="Spurious"> + * <p>The atomic classes also support method {@code weakCompareAndSet}, + * which has limited applicability. On some platforms, the weak version + * may be more efficient than {@code compareAndSet} in the normal case, + * but differs in that any given invocation of the + * {@code weakCompareAndSet} method may return {@code false} + * <em>spuriously</em> (that is, for no apparent reason)</a>. A + * {@code false} return means only that the operation may be retried if + * desired, relying on the guarantee that repeated invocation when the + * variable holds {@code expectedValue} and no other thread is also + * attempting to set the variable will eventually succeed. (Such + * spurious failures may for example be due to memory contention effects + * that are unrelated to whether the expected and current values are + * equal.) Additionally {@code weakCompareAndSet} does not provide + * ordering guarantees that are usually needed for synchronization + * control. However, the method may be useful for updating counters and + * statistics when such updates are unrelated to the other + * happens-before orderings of a program. When a thread sees an update + * to an atomic variable caused by a {@code weakCompareAndSet}, it does + * not necessarily see updates to any <em>other</em> variables that + * occurred before the {@code weakCompareAndSet}. This may be + * acceptable when, for example, updating performance statistics, but + * rarely otherwise. + * + * <p>The {@link java.util.concurrent.atomic.AtomicMarkableReference} + * class associates a single boolean with a reference. For example, this + * bit might be used inside a data structure to mean that the object + * being referenced has logically been deleted. + * + * The {@link java.util.concurrent.atomic.AtomicStampedReference} + * class associates an integer value with a reference. This may be + * used for example, to represent version numbers corresponding to + * series of updates. + * + * <p>Atomic classes are designed primarily as building blocks for + * implementing non-blocking data structures and related infrastructure + * classes. The {@code compareAndSet} method is not a general + * replacement for locking. It applies only when critical updates for an + * object are confined to a <em>single</em> variable. + * + * <p>Atomic classes are not general purpose replacements for + * {@code java.lang.Integer} and related classes. They do <em>not</em> + * define methods such as {@code hashCode} and + * {@code compareTo}. (Because atomic variables are expected to be + * mutated, they are poor choices for hash table keys.) Additionally, + * classes are provided only for those types that are commonly useful in + * intended applications. For example, there is no atomic class for + * representing {@code byte}. In those infrequent cases where you would + * like to do so, you can use an {@code AtomicInteger} to hold + * {@code byte} values, and cast appropriately. + * + * You can also hold floats using + * {@link java.lang.Float#floatToIntBits} and + * {@link java.lang.Float#intBitsToFloat} conversions, and doubles using + * {@link java.lang.Double#doubleToLongBits} and + * {@link java.lang.Double#longBitsToDouble} conversions. + * + * @since 1.5 + */ +package java.util.concurrent.atomic; diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/package.html b/concurrent/src/main/java/java/util/concurrent/atomic/package.html deleted file mode 100644 index bdb15e8..0000000 --- a/concurrent/src/main/java/java/util/concurrent/atomic/package.html +++ /dev/null @@ -1,132 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> -<html> <head> -<title>Atomics</title> -</head> - -<body> - -A small toolkit of classes that support lock-free thread-safe -programming on single variables. In essence, the classes in this -package extend the notion of <tt>volatile</tt> values, fields, and -array elements to those that also provide an atomic conditional update -operation of the form: - -<pre> - boolean compareAndSet(expectedValue, updateValue); -</pre> - -<p> This method (which varies in argument types across different -classes) atomically sets a variable to the <tt>updateValue</tt> if it -currently holds the <tt>expectedValue</tt>, reporting <tt>true</tt> on -success. The classes in this package also contain methods to get and -unconditionally set values, as well as a weaker conditional atomic -update operation <tt> weakCompareAndSet</tt>. The weak version may be -more efficient in the normal case, but differs in that any given -invocation of <tt>weakCompareAndSet</tt> method may fail, even -spuriously (that is, for no apparent reason). A <tt>false</tt> return -means only that the operation may be retried if desired, relying on -the guarantee that repeated invocation when the variable holds -<tt>expectedValue</tt> and no other thread is also attempting to set -the variable will eventually succeed. - -<p> The specifications of these methods enable implementations to -employ efficient machine-level atomic instructions that are available -on contemporary processors. However on some platforms, support may -entail some form of internal locking. Thus the methods are not -strictly guaranteed to be non-blocking -- -a thread may block transiently before performing the operation. - -<p> Instances of classes {@link -java.util.concurrent.atomic.AtomicBoolean}, {@link -java.util.concurrent.atomic.AtomicInteger}, {@link -java.util.concurrent.atomic.AtomicLong}, and {@link -java.util.concurrent.atomic.AtomicReference} each provide access and -updates to a single variable of the corresponding type. Each class -also provides appropriate utility methods for that type. For example, -classes <tt>AtomicLong</tt> and <tt>AtomicInteger</tt> provide atomic -increment methods. One application is to generate sequence numbers, -as in: - -<pre> -class Sequencer { - private AtomicLong sequenceNumber = new AtomicLong(0); - public long next() { return sequenceNumber.getAndIncrement(); } -} -</pre> - -<p>The memory effects for accesses and updates of atomics generally follow the -rules for volatiles: - -<ul> - - <li> <tt>get</tt> has the memory effects of reading a -<tt>volatile</tt> variable. - - <li> <tt>set</tt> has the memory effects of writing (assigning) a -<tt>volatile</tt> variable. - - <li><tt>weakCompareAndSet</tt> atomically reads and conditionally - writes a variable, is ordered with respect to other - memory operations on that variable, but otherwise acts as an - ordinary non-volatile memory operation. - - <li> <tt>compareAndSet</tt> - and all other read-and-update operations such as <tt>getAndIncrement</tt> - have the memory effects of both reading and - writing <tt>volatile</tt> variables. -</ul> - -<p>In addition to classes representing single values, this package -contains <em>Updater</em> classes that can be used to obtain -<tt>compareAndSet</tt> operations on any selected <tt>volatile</tt> -field of any selected class. {@link -java.util.concurrent.atomic.AtomicReferenceFieldUpdater}, {@link -java.util.concurrent.atomic.AtomicIntegerFieldUpdater}, and {@link -java.util.concurrent.atomic.AtomicLongFieldUpdater} are -reflection-based utilities that provide access to the associated field -types. These are mainly of use in atomic data structures in which -several <tt>volatile</tt> fields of the same node (for example, the -links of a tree node) are independently subject to atomic -updates. These classes enable greater flexibility in how and when to -use atomic updates, at the expense of more awkward reflection-based -setup, less convenient usage, and weaker guarantees. - -<p>The {@link java.util.concurrent.atomic.AtomicIntegerArray}, {@link -java.util.concurrent.atomic.AtomicLongArray}, and {@link -java.util.concurrent.atomic.AtomicReferenceArray} classes further -extend atomic operation support to arrays of these types. These -classes are also notable in providing <tt>volatile</tt> access -semantics for their array elements, which is not supported for -ordinary arrays. - -<p> The {@link java.util.concurrent.atomic.AtomicMarkableReference} -class associates a single boolean with a reference. For example, this -bit might be used inside a data structure to mean that the object -being referenced has logically been deleted. The {@link -java.util.concurrent.atomic.AtomicStampedReference} class associates -an integer value with a reference. This may be used for example, to -represent version numbers corresponding to series of updates. - -<p> Atomic classes are designed primarily as building blocks for -implementing non-blocking data structures and related infrastructure -classes. The <tt>compareAndSet</tt> method is not a general -replacement for locking. It applies only when critical updates for an -object are confined to a <em>single</em> variable. - -<p> Atomic classes are not general purpose replacements for -<tt>java.lang.Integer</tt> and related classes. They do <em>not</em> -define methods such as <tt>hashCode</tt> and -<tt>compareTo</tt>. (Because atomic variables are expected to be -mutated, they are poor choices for hash table keys.) Additionally, -classes are provided only for those types that are commonly useful in -intended applications. For example, there is no atomic class for -representing <tt>byte</tt>. In those infrequent cases where you would -like to do so, you can use an <tt>AtomicInteger</tt> to hold -<tt>byte</tt> values, and cast appropriately. You can also hold floats -using <tt>Float.floatToIntBits</tt> and <tt>Float.intBitstoFloat</tt> -conversions, and doubles using <tt>Double.doubleToLongBits</tt> and -<tt>Double.longBitsToDouble</tt> conversions. - -@since Android 1.0 - -</body> </html> diff --git a/concurrent/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java b/concurrent/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java new file mode 100644 index 0000000..bf2fe16 --- /dev/null +++ b/concurrent/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java @@ -0,0 +1,56 @@ +/* + * 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 java.util.concurrent.locks; + +/** + * A synchronizer that may be exclusively owned by a thread. This + * class provides a basis for creating locks and related synchronizers + * that may entail a notion of ownership. The + * <tt>AbstractOwnableSynchronizer</tt> class itself does not manage or + * use this information. However, subclasses and tools may use + * appropriately maintained values to help control and monitor access + * and provide diagnostics. + * + * @author Doug Lea + */ +abstract class AbstractOwnableSynchronizer + implements java.io.Serializable { + + /** Use serial ID even though all fields transient. */ + private static final long serialVersionUID = 3737899427754241961L; + + /** + * Empty constructor for use by subclasses. + */ + protected AbstractOwnableSynchronizer() { } + + /** + * The current owner of exclusive mode synchronization. + */ + private transient Thread exclusiveOwnerThread; + + /** + * Sets the thread that currently owns exclusive access. A + * <tt>null</tt> argument indicates that no thread owns access. + * This method does not otherwise impose any synchronization or + * <tt>volatile</tt> field accesses. + */ + protected final void setExclusiveOwnerThread(Thread t) { + exclusiveOwnerThread = t; + } + + /** + * Returns the thread last set by + * <tt>setExclusiveOwnerThread</tt>, or <tt>null</tt> if never + * set. This method does not otherwise impose any synchronization + * or <tt>volatile</tt> field accesses. + * @return the owner thread + */ + protected final Thread getExclusiveOwnerThread() { + return exclusiveOwnerThread; + } +}
\ No newline at end of file diff --git a/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java index 29e72b5..8b9821e 100644 --- a/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java +++ b/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java @@ -32,7 +32,7 @@ import sun.misc.Unsafe; * synchronization interface. Instead it defines methods such as * {@link #acquireInterruptibly} that can be invoked as * appropriate by concrete locks and related synchronizers to - * implement their public methods. + * implement their public methods. * * <p>This class supports either or both a default <em>exclusive</em> * mode and a <em>shared</em> mode. When acquired in exclusive mode, @@ -59,14 +59,14 @@ import sun.misc.Unsafe; * condition, so if this constraint cannot be met, do not use it. The * behavior of {@link ConditionObject} depends of course on the * semantics of its synchronizer implementation. - * - * <p> This class provides inspection, instrumentation, and monitoring + * + * <p>This class provides inspection, instrumentation, and monitoring * methods for the internal queue, as well as similar methods for * condition objects. These can be exported as desired into classes * using an <tt>AbstractQueuedSynchronizer</tt> for their * synchronization mechanics. * - * <p> Serialization of this class stores only the underlying atomic + * <p>Serialization of this class stores only the underlying atomic * integer maintaining state, so deserialized objects have empty * thread queues. Typical subclasses requiring serializability will * define a <tt>readObject</tt> method that restores this to a known @@ -74,10 +74,10 @@ import sun.misc.Unsafe; * * <h3>Usage</h3> * - * <p> To use this class as the basis of a synchronizer, redefine the + * <p>To use this class as the basis of a synchronizer, redefine the * following methods, as applicable, by inspecting and/or modifying * the synchronization state using {@link #getState}, {@link - * #setState} and/or {@link #compareAndSetState}: + * #setState} and/or {@link #compareAndSetState}: * * <ul> * <li> {@link #tryAcquire} @@ -94,7 +94,7 @@ import sun.misc.Unsafe; * means of using this class. All other methods are declared * <tt>final</tt> because they cannot be independently varied. * - * <p> Even though this class is based on an internal FIFO queue, it + * <p>Even though this class is based on an internal FIFO queue, it * does not automatically enforce FIFO acquisition policies. The core * of exclusive synchronization takes the form: * @@ -112,22 +112,17 @@ import sun.misc.Unsafe; * * (Shared mode is similar but may involve cascading signals.) * - * <p> Because checks in acquire are invoked before enqueuing, a newly - * acquiring thread may <em>barge</em> ahead of others that are - * blocked and queued. However, you can, if desired, define - * <tt>tryAcquire</tt> and/or <tt>tryAcquireShared</tt> to disable - * barging by internally invoking one or more of the inspection - * methods. In particular, a strict FIFO lock can define - * <tt>tryAcquire</tt> to immediately return <tt>false</tt> if {@link - * #getFirstQueuedThread} does not return the current thread. A - * normally preferable non-strict fair version can immediately return - * <tt>false</tt> only if {@link #hasQueuedThreads} returns - * <tt>true</tt> and <tt>getFirstQueuedThread</tt> is not the current - * thread; or equivalently, that <tt>getFirstQueuedThread</tt> is both - * non-null and not the current thread. Further variations are - * possible. + * <p><a name="barging">Because checks in acquire are invoked before + * enqueuing, a newly acquiring thread may <em>barge</em> ahead of + * others that are blocked and queued. However, you can, if desired, + * define <tt>tryAcquire</tt> and/or <tt>tryAcquireShared</tt> to + * disable barging by internally invoking one or more of the inspection + * methods, thereby providing a <em>fair</em> FIFO acquisition order. + * In particular, most fair synchronizers can define <tt>tryAcquire</tt> + * to return <tt>false</tt> if predecessors are queued. Other variations + * are possible. * - * <p> Throughput and scalability are generally highest for the + * <p>Throughput and scalability are generally highest for the * default barging (also known as <em>greedy</em>, * <em>renouncement</em>, and <em>convoy-avoidance</em>) strategy. * While this is not guaranteed to be fair or starvation-free, earlier @@ -144,7 +139,7 @@ import sun.misc.Unsafe; * and/or {@link #hasQueuedThreads} to only do so if the synchronizer * is likely not to be contended. * - * <p> This class provides an efficient and scalable basis for + * <p>This class provides an efficient and scalable basis for * synchronization in part by specializing its range of use to * synchronizers that can rely on <tt>int</tt> state, acquire, and * release parameters, and an internal FIFO wait queue. When this does @@ -152,7 +147,7 @@ import sun.misc.Unsafe; * {@link java.util.concurrent.atomic atomic} classes, your own custom * {@link java.util.Queue} classes, and {@link LockSupport} blocking * support. - * + * * <h3>Usage Examples</h3> * * <p>Here is a non-reentrant mutual exclusion lock class that uses @@ -163,56 +158,58 @@ import sun.misc.Unsafe; * <pre> * class Mutex implements Lock, java.io.Serializable { * - * // Our internal helper class - * private static class Sync extends AbstractQueuedSynchronizer { - * // Report whether in locked state - * protected boolean isHeldExclusively() { - * return getState() == 1; - * } + * // Our internal helper class + * private static class Sync extends AbstractQueuedSynchronizer { + * // Report whether in locked state + * protected boolean isHeldExclusively() { + * return getState() == 1; + * } * - * // Acquire the lock if state is zero - * public boolean tryAcquire(int acquires) { - * assert acquires == 1; // Otherwise unused - * return compareAndSetState(0, 1); - * } + * // Acquire the lock if state is zero + * public boolean tryAcquire(int acquires) { + * assert acquires == 1; // Otherwise unused + * return compareAndSetState(0, 1); + * } * - * // Release the lock by setting state to zero - * protected boolean tryRelease(int releases) { - * assert releases == 1; // Otherwise unused - * if (getState() == 0) throw new IllegalMonitorStateException(); - * setState(0); - * return true; - * } - * - * // Provide a Condition - * Condition newCondition() { return new ConditionObject(); } + * // Release the lock by setting state to zero + * protected boolean tryRelease(int releases) { + * assert releases == 1; // Otherwise unused + * if (getState() == 0) throw new IllegalMonitorStateException(); + * setState(0); + * return true; + * } + * + * // Provide a Condition + * Condition newCondition() { return new ConditionObject(); } * - * // Deserialize properly - * private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { - * s.defaultReadObject(); - * setState(0); // reset to unlocked state - * } - * } + * // Deserialize properly + * private void readObject(ObjectInputStream s) + * throws IOException, ClassNotFoundException { + * s.defaultReadObject(); + * setState(0); // reset to unlocked state + * } + * } * - * // The sync object does all the hard work. We just forward to it. - * private final Sync sync = new Sync(); + * // The sync object does all the hard work. We just forward to it. + * private final Sync sync = new Sync(); * - * public void lock() { sync.acquire(1); } - * public boolean tryLock() { return sync.tryAcquire(1); } - * public void unlock() { sync.release(1); } - * public Condition newCondition() { return sync.newCondition(); } - * public boolean isLocked() { return sync.isHeldExclusively(); } - * public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } - * public void lockInterruptibly() throws InterruptedException { - * sync.acquireInterruptibly(1); - * } - * public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { - * return sync.tryAcquireNanos(1, unit.toNanos(timeout)); - * } + * public void lock() { sync.acquire(1); } + * public boolean tryLock() { return sync.tryAcquire(1); } + * public void unlock() { sync.release(1); } + * public Condition newCondition() { return sync.newCondition(); } + * public boolean isLocked() { return sync.isHeldExclusively(); } + * public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } + * public void lockInterruptibly() throws InterruptedException { + * sync.acquireInterruptibly(1); + * } + * public boolean tryLock(long timeout, TimeUnit unit) + * throws InterruptedException { + * return sync.tryAcquireNanos(1, unit.toNanos(timeout)); + * } * } * </pre> * - * <p> Here is a latch class that is like a {@link CountDownLatch} + * <p>Here is a latch class that is like a {@link CountDownLatch} * except that it only requires a single <tt>signal</tt> to * fire. Because a latch is non-exclusive, it uses the <tt>shared</tt> * acquire and release methods. @@ -220,33 +217,35 @@ import sun.misc.Unsafe; * <pre> * class BooleanLatch { * - * private static class Sync extends AbstractQueuedSynchronizer { - * boolean isSignalled() { return getState() != 0; } + * private static class Sync extends AbstractQueuedSynchronizer { + * boolean isSignalled() { return getState() != 0; } * - * protected int tryAcquireShared(int ignore) { - * return isSignalled()? 1 : -1; - * } - * - * protected boolean tryReleaseShared(int ignore) { - * setState(1); - * return true; - * } - * } + * protected int tryAcquireShared(int ignore) { + * return isSignalled()? 1 : -1; + * } * - * private final Sync sync = new Sync(); - * public boolean isSignalled() { return sync.isSignalled(); } - * public void signal() { sync.releaseShared(1); } - * public void await() throws InterruptedException { - * sync.acquireSharedInterruptibly(1); - * } - * } + * protected boolean tryReleaseShared(int ignore) { + * setState(1); + * return true; + * } + * } * + * private final Sync sync = new Sync(); + * public boolean isSignalled() { return sync.isSignalled(); } + * public void signal() { sync.releaseShared(1); } + * public void await() throws InterruptedException { + * sync.acquireSharedInterruptibly(1); + * } + * } * </pre> * * @since 1.5 * @author Doug Lea */ -public abstract class AbstractQueuedSynchronizer implements java.io.Serializable { +public abstract class AbstractQueuedSynchronizer + extends AbstractOwnableSynchronizer + implements java.io.Serializable { + private static final long serialVersionUID = 7373984972572414691L; /** @@ -258,7 +257,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable /** * Wait queue node class. * - * <p> The wait queue is a variant of a "CLH" (Craig, Landin, and + * <p>The wait queue is a variant of a "CLH" (Craig, Landin, and * Hagersten) lock queue. CLH locks are normally used for * spinlocks. We instead use them for blocking synchronizers, but * use the same basic tactic of holding some of the control @@ -274,7 +273,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * contender thread may need to rewait. * * <p>To enqueue into a CLH lock, you atomically splice it in as new - * tail. To dequeue, you just set the head field. + * tail. To dequeue, you just set the head field. * <pre> * +------+ prev +-----+ +-----+ * head | | <---- | | <---- | | tail @@ -295,7 +294,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * predecessor. For explanation of similar mechanics in the case * of spin locks, see the papers by Scott and Scherer at * http://www.cs.rochester.edu/u/scott/synchronization/ - * + * * <p>We also use "next" links to implement blocking mechanics. * The thread id for each node is kept in its own node, so a * predecessor signals the next node to wake up by traversing @@ -312,7 +311,8 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * nodes, we can miss noticing whether a cancelled node is * ahead or behind us. This is dealt with by always unparking * successors upon cancellation, allowing them to stabilize on - * a new predecessor. + * a new predecessor, unless we can identify an uncancelled + * predecessor who will carry this responsibility. * * <p>CLH queues need a dummy header node to get started. But * we don't create them on construction, because it would be wasted @@ -334,34 +334,46 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * on the design of this class. */ static final class Node { + /** Marker to indicate a node is waiting in shared mode */ + static final Node SHARED = new Node(); + /** Marker to indicate a node is waiting in exclusive mode */ + static final Node EXCLUSIVE = null; + /** waitStatus value to indicate thread has cancelled */ static final int CANCELLED = 1; - /** waitStatus value to indicate thread needs unparking */ + /** waitStatus value to indicate successor's thread needs unparking */ static final int SIGNAL = -1; /** waitStatus value to indicate thread is waiting on condition */ static final int CONDITION = -2; - /** Marker to indicate a node is waiting in shared mode */ - static final Node SHARED = new Node(); - /** Marker to indicate a node is waiting in exclusive mode */ - static final Node EXCLUSIVE = null; + /** + * waitStatus value to indicate the next acquireShared should + * unconditionally propagate + */ + static final int PROPAGATE = -3; /** * Status field, taking on only the values: - * SIGNAL: The successor of this node is (or will soon be) - * blocked (via park), so the current node must - * unpark its successor when it releases or + * SIGNAL: The successor of this node is (or will soon be) + * blocked (via park), so the current node must + * unpark its successor when it releases or * cancels. To avoid races, acquire methods must - * first indicate they need a signal, - * then retry the atomic acquire, and then, + * first indicate they need a signal, + * then retry the atomic acquire, and then, * on failure, block. - * CANCELLED: Node is cancelled due to timeout or interrupt + * CANCELLED: This node is cancelled due to timeout or interrupt. * Nodes never leave this state. In particular, * a thread with cancelled node never again blocks. - * CONDITION: Node is currently on a condition queue - * It will not be used as a sync queue node until - * transferred. (Use of this value here - * has nothing to do with the other uses - * of the field, but simplifies mechanics.) + * CONDITION: This node is currently on a condition queue. + * It will not be used as a sync queue node + * until transferred, at which time the status + * will be set to 0. (Use of this value here has + * nothing to do with the other uses of the + * field, but simplifies mechanics.) + * PROPAGATE: A releaseShared should be propagated to other + * nodes. This is set (for head node only) in + * doReleaseShared to ensure propagation + * continues, even if other operations have + * since intervened. * 0: None of the above * * The values are arranged numerically to simplify use. @@ -370,8 +382,8 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * values, just for sign. * * The field is initialized to 0 for normal sync nodes, and - * CONDITION for condition nodes. It is modified only using - * CAS. + * CONDITION for condition nodes. It is modified using CAS + * (or when possible, unconditional volatile writes). */ volatile int waitStatus; @@ -390,25 +402,26 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable /** * Link to the successor node that the current node/thread - * unparks upon release. Assigned once during enqueuing, and - * nulled out (for sake of GC) when no longer needed. Upon - * cancellation, we cannot adjust this field, but can notice - * status and bypass the node if cancelled. The enq operation - * does not assign next field of a predecessor until after - * attachment, so seeing a null next field does not - * necessarily mean that node is at end of queue. However, if - * a next field appears to be null, we can scan prev's from - * the tail to double-check. + * unparks upon release. Assigned during enqueuing, adjusted + * when bypassing cancelled predecessors, and nulled out (for + * sake of GC) when dequeued. The enq operation does not + * assign next field of a predecessor until after attachment, + * so seeing a null next field does not necessarily mean that + * node is at end of queue. However, if a next field appears + * to be null, we can scan prev's from the tail to + * double-check. The next field of cancelled nodes is set to + * point to the node itself instead of null, to make life + * easier for isOnSyncQueue. */ volatile Node next; /** * The thread that enqueued this node. Initialized on - * construction and nulled out after use. + * construction and nulled out after use. */ volatile Thread thread; - /** + /** * Link to next node waiting on condition, or the special * value SHARED. Because condition queues are accessed only * when holding in exclusive mode, we just need a simple @@ -428,14 +441,16 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable } /** - * Returns previous node, or throws NullPointerException if - * null. Use when predecessor cannot be null. + * Returns previous node, or throws NullPointerException if null. + * Use when predecessor cannot be null. The null check could + * be elided, but is present to help the VM. + * * @return the predecessor of this node */ final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) - throw new NullPointerException(); + throw new NullPointerException(); else return p; } @@ -450,11 +465,11 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable Node(Thread thread, int waitStatus) { // Used by Condition this.waitStatus = waitStatus; - this.thread = thread; + this.thread = thread; } } - /** + /** * Head of the wait queue, lazily initialized. Except for * initialization, it is modified only via method setHead. Note: * If head exists, its waitStatus is guaranteed not to be @@ -462,11 +477,11 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable */ private transient volatile Node head; - /** + /** * Tail of the wait queue, lazily initialized. Modified only via * method enq to add new wait node. */ - private transient volatile Node tail; + private transient volatile Node tail; /** * The synchronization state. @@ -496,10 +511,11 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * value if the current state value equals the expected value. * This operation has memory semantics of a <tt>volatile</tt> read * and write. + * * @param expect the expected value * @param update the new value - * @return true if successful. False return indicates that - * the actual value was not equal to the expected value. + * @return true if successful. False return indicates that the actual + * value was not equal to the expected value. */ protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this @@ -509,7 +525,14 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable // Queuing utilities /** - * Insert node into queue, initializing if necessary. See picture above. + * The number of nanoseconds for which it is faster to spin + * rather than to use timed park. A rough estimate suffices + * to improve responsiveness with very short timeouts. + */ + static final long spinForTimeoutThreshold = 1000L; + + /** + * Inserts node into queue, initializing if necessary. See picture above. * @param node the node to insert * @return node's predecessor */ @@ -517,27 +540,21 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable for (;;) { Node t = tail; if (t == null) { // Must initialize - Node h = new Node(); // Dummy header - h.next = node; - node.prev = h; - if (compareAndSetHead(h)) { - tail = node; - return h; - } - } - else { - node.prev = t; + if (compareAndSetHead(new Node())) + tail = head; + } else { + node.prev = t; if (compareAndSetTail(t, node)) { - t.next = node; - return t; + t.next = node; + return t; } } } } /** - * Create and enq node for given thread and mode - * @param current the thread + * Creates and enqueues node for current thread and given mode. + * * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared * @return the new node */ @@ -546,9 +563,9 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable // Try the fast path of enq; backup to full enq on failure Node pred = tail; if (pred != null) { - node.prev = pred; + node.prev = pred; if (compareAndSetTail(pred, node)) { - pred.next = node; + pred.next = node; return node; } } @@ -557,79 +574,166 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable } /** - * Set head of queue to be node, thus dequeuing. Called only by + * Sets head of queue to be node, thus dequeuing. Called only by * acquire methods. Also nulls out unused fields for sake of GC * and to suppress unnecessary signals and traversals. - * @param node the node + * + * @param node the node */ private void setHead(Node node) { head = node; node.thread = null; - node.prev = null; + node.prev = null; } /** - * Wake up node's successor, if one exists. + * Wakes up node's successor, if one exists. + * * @param node the node */ private void unparkSuccessor(Node node) { /* - * Try to clear status in anticipation of signalling. It is - * OK if this fails or if status is changed by waiting thread. + * If status is negative (i.e., possibly needing signal) try + * to clear in anticipation of signalling. It is OK if this + * fails or if status is changed by waiting thread. */ - compareAndSetWaitStatus(node, Node.SIGNAL, 0); - + int ws = node.waitStatus; + if (ws < 0) + compareAndSetWaitStatus(node, ws, 0); + /* * Thread to unpark is held in successor, which is normally * just the next node. But if cancelled or apparently null, * traverse backwards from tail to find the actual * non-cancelled successor. */ - Thread thread; Node s = node.next; - if (s != null && s.waitStatus <= 0) - thread = s.thread; - else { - thread = null; - for (s = tail; s != null && s != node; s = s.prev) - if (s.waitStatus <= 0) - thread = s.thread; + if (s == null || s.waitStatus > 0) { + s = null; + for (Node t = tail; t != null && t != node; t = t.prev) + if (t.waitStatus <= 0) + s = t; } - LockSupport.unpark(thread); + if (s != null) + LockSupport.unpark(s.thread); } /** - * Set head of queue, and check if successor may be waiting - * in shared mode, if so propagating if propagate > 0. - * @param pred the node holding waitStatus for node - * @param node the node + * Release action for shared mode -- signal successor and ensure + * propagation. (Note: For exclusive mode, release just amounts + * to calling unparkSuccessor of head if it needs signal.) + */ + private void doReleaseShared() { + /* + * Ensure that a release propagates, even if there are other + * in-progress acquires/releases. This proceeds in the usual + * way of trying to unparkSuccessor of head if it needs + * signal. But if it does not, status is set to PROPAGATE to + * ensure that upon release, propagation continues. + * Additionally, we must loop in case a new node is added + * while we are doing this. Also, unlike other uses of + * unparkSuccessor, we need to know if CAS to reset status + * fails, if so rechecking. + */ + for (;;) { + Node h = head; + if (h != null && h != tail) { + int ws = h.waitStatus; + if (ws == Node.SIGNAL) { + if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) + continue; // loop to recheck cases + unparkSuccessor(h); + } + else if (ws == 0 && + !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) + continue; // loop on failed CAS + } + if (h == head) // loop if head changed + break; + } + } + + /** + * Sets head of queue, and checks if successor may be waiting + * in shared mode, if so propagating if either propagate > 0 or + * PROPAGATE status was set. + * + * @param node the node * @param propagate the return value from a tryAcquireShared */ private void setHeadAndPropagate(Node node, int propagate) { + Node h = head; // Record old head for check below setHead(node); - if (propagate > 0 && node.waitStatus != 0) { - /* - * Don't bother fully figuring out successor. If it - * looks null, call unparkSuccessor anyway to be safe. - */ - Node s = node.next; + /* + * Try to signal next queued node if: + * Propagation was indicated by caller, + * or was recorded (as h.waitStatus) by a previous operation + * (note: this uses sign-check of waitStatus because + * PROPAGATE status may transition to SIGNAL.) + * and + * The next node is waiting in shared mode, + * or we don't know, because it appears null + * + * The conservatism in both of these checks may cause + * unnecessary wake-ups, but only when there are multiple + * racing acquires/releases, so most need signals now or soon + * anyway. + */ + if (propagate > 0 || h == null || h.waitStatus < 0) { + Node s = node.next; if (s == null || s.isShared()) - unparkSuccessor(node); + doReleaseShared(); } } // Utilities for various versions of acquire /** - * Cancel an ongoing attempt to acquire. + * Cancels an ongoing attempt to acquire. + * * @param node the node */ private void cancelAcquire(Node node) { - if (node != null) { // Ignore if node doesn't exist - node.thread = null; - // Can use unconditional write instead of CAS here - node.waitStatus = Node.CANCELLED; - unparkSuccessor(node); + // Ignore if node doesn't exist + if (node == null) + return; + + node.thread = null; + + // Skip cancelled predecessors + Node pred = node.prev; + while (pred.waitStatus > 0) + node.prev = pred = pred.prev; + + // predNext is the apparent node to unsplice. CASes below will + // fail if not, in which case, we lost race vs another cancel + // or signal, so no further action is necessary. + Node predNext = pred.next; + + // Can use unconditional write instead of CAS here. + // After this atomic step, other Nodes can skip past us. + // Before, we are free of interference from other threads. + node.waitStatus = Node.CANCELLED; + + // If we are the tail, remove ourselves. + if (node == tail && compareAndSetTail(node, pred)) { + compareAndSetNext(pred, predNext, null); + } else { + // If successor needs signal, try to set pred's next-link + // so it will get one. Otherwise wake it up to propagate. + int ws; + if (pred != head && + ((ws = pred.waitStatus) == Node.SIGNAL || + (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && + pred.thread != null) { + Node next = node.next; + if (next != null && next.waitStatus <= 0) + compareAndSetNext(pred, predNext, next); + } else { + unparkSuccessor(node); + } + + node.next = node; // help GC } } @@ -637,31 +741,36 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * Checks and updates status for a node that failed to acquire. * Returns true if thread should block. This is the main signal * control in all acquire loops. Requires that pred == node.prev + * * @param pred node's predecessor holding status - * @param node the node - * @return true if thread should block + * @param node the node + * @return {@code true} if thread should block */ private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { - int s = pred.waitStatus; - if (s < 0) + int ws = pred.waitStatus; + if (ws == Node.SIGNAL) /* * This node has already set status asking a release - * to signal it, so it can safely park + * to signal it, so it can safely park. */ return true; - if (s > 0) + if (ws > 0) { /* - * Predecessor was cancelled. Move up to its predecessor - * and indicate retry. + * Predecessor was cancelled. Skip over predecessors and + * indicate retry. */ - node.prev = pred.prev; - else + do { + node.prev = pred = pred.prev; + } while (pred.waitStatus > 0); + pred.next = node; + } else { /* - * Indicate that we need a signal, but don't park yet. Caller - * will need to retry to make sure it cannot acquire before - * parking. + * waitStatus must be 0 or PROPAGATE. Indicate that we + * need a signal, but don't park yet. Caller will need to + * retry to make sure it cannot acquire before parking. */ - compareAndSetWaitStatus(pred, 0, Node.SIGNAL); + compareAndSetWaitStatus(pred, ws, Node.SIGNAL); + } return false; } @@ -674,9 +783,10 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable /** * Convenience method to park and then check if interrupted - * @return true if interrupted + * + * @return {@code true} if interrupted */ - private static boolean parkAndCheckInterrupt() { + private final boolean parkAndCheckInterrupt() { LockSupport.park(); return Thread.interrupted(); } @@ -687,17 +797,19 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * different. Only a little bit of factoring is possible due to * interactions of exception mechanics (including ensuring that we * cancel if tryAcquire throws exception) and other control, at - * least not without hurting performance too much. + * least not without hurting performance too much. */ /** - * Acquire in exclusive uninterruptible mode for thread already in + * Acquires in exclusive uninterruptible mode for thread already in * queue. Used by condition wait methods as well as acquire. + * * @param node the node * @param arg the acquire argument - * @return true if interrupted while waiting + * @return {@code true} if interrupted while waiting */ final boolean acquireQueued(final Node node, int arg) { + boolean failed = true; try { boolean interrupted = false; for (;;) { @@ -705,92 +817,91 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC + failed = false; return interrupted; } - if (shouldParkAfterFailedAcquire(p, node) && - parkAndCheckInterrupt()) + if (shouldParkAfterFailedAcquire(p, node) && + parkAndCheckInterrupt()) interrupted = true; } - } catch (RuntimeException ex) { - cancelAcquire(node); - throw ex; + } finally { + if (failed) + cancelAcquire(node); } } - /** - * Acquire in exclusive interruptible mode + /** + * Acquires in exclusive interruptible mode. * @param arg the acquire argument */ - private void doAcquireInterruptibly(int arg) + private void doAcquireInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.EXCLUSIVE); + boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC + failed = false; return; } - if (shouldParkAfterFailedAcquire(p, node) && - parkAndCheckInterrupt()) - break; + if (shouldParkAfterFailedAcquire(p, node) && + parkAndCheckInterrupt()) + throw new InterruptedException(); } - } catch (RuntimeException ex) { - cancelAcquire(node); - throw ex; + } finally { + if (failed) + cancelAcquire(node); } - // Arrive here only if interrupted - cancelAcquire(node); - throw new InterruptedException(); } - /** - * Acquire in exclusive timed mode + /** + * Acquires in exclusive timed mode. + * * @param arg the acquire argument * @param nanosTimeout max wait time - * @return true if acquired + * @return {@code true} if acquired */ - private boolean doAcquireNanos(int arg, long nanosTimeout) + private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { long lastTime = System.nanoTime(); final Node node = addWaiter(Node.EXCLUSIVE); + boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC + failed = false; return true; } - if (nanosTimeout <= 0) { - cancelAcquire(node); + if (nanosTimeout <= 0) return false; - } - if (shouldParkAfterFailedAcquire(p, node)) { + if (shouldParkAfterFailedAcquire(p, node) && + nanosTimeout > spinForTimeoutThreshold) LockSupport.parkNanos(nanosTimeout); - if (Thread.interrupted()) - break; - long now = System.nanoTime(); - nanosTimeout -= now - lastTime; - lastTime = now; - } + long now = System.nanoTime(); + nanosTimeout -= now - lastTime; + lastTime = now; + if (Thread.interrupted()) + throw new InterruptedException(); } - } catch (RuntimeException ex) { - cancelAcquire(node); - throw ex; + } finally { + if (failed) + cancelAcquire(node); } - // Arrive here only if interrupted - cancelAcquire(node); - throw new InterruptedException(); } - /** - * Acquire in shared uninterruptible mode + /** + * Acquires in shared uninterruptible mode. * @param arg the acquire argument */ private void doAcquireShared(int arg) { final Node node = addWaiter(Node.SHARED); + boolean failed = true; try { boolean interrupted = false; for (;;) { @@ -802,26 +913,28 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable p.next = null; // help GC if (interrupted) selfInterrupt(); + failed = false; return; } } - if (shouldParkAfterFailedAcquire(p, node) && - parkAndCheckInterrupt()) + if (shouldParkAfterFailedAcquire(p, node) && + parkAndCheckInterrupt()) interrupted = true; } - } catch (RuntimeException ex) { - cancelAcquire(node); - throw ex; + } finally { + if (failed) + cancelAcquire(node); } } - /** - * Acquire in shared interruptible mode + /** + * Acquires in shared interruptible mode. * @param arg the acquire argument */ - private void doAcquireSharedInterruptibly(int arg) + private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); + boolean failed = true; try { for (;;) { final Node p = node.predecessor(); @@ -830,33 +943,33 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC + failed = false; return; } } - if (shouldParkAfterFailedAcquire(p, node) && - parkAndCheckInterrupt()) - break; + if (shouldParkAfterFailedAcquire(p, node) && + parkAndCheckInterrupt()) + throw new InterruptedException(); } - } catch (RuntimeException ex) { - cancelAcquire(node); - throw ex; + } finally { + if (failed) + cancelAcquire(node); } - // Arrive here only if interrupted - cancelAcquire(node); - throw new InterruptedException(); } - /** - * Acquire in shared timed mode + /** + * Acquires in shared timed mode. + * * @param arg the acquire argument * @param nanosTimeout max wait time - * @return true if acquired + * @return {@code true} if acquired */ - private boolean doAcquireSharedNanos(int arg, long nanosTimeout) + private boolean doAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException { long lastTime = System.nanoTime(); final Node node = addWaiter(Node.SHARED); + boolean failed = true; try { for (;;) { final Node p = node.predecessor(); @@ -865,32 +978,28 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC + failed = false; return true; } } - if (nanosTimeout <= 0) { - cancelAcquire(node); + if (nanosTimeout <= 0) return false; - } - if (shouldParkAfterFailedAcquire(p, node)) { + if (shouldParkAfterFailedAcquire(p, node) && + nanosTimeout > spinForTimeoutThreshold) LockSupport.parkNanos(nanosTimeout); - if (Thread.interrupted()) - break; - long now = System.nanoTime(); - nanosTimeout -= now - lastTime; - lastTime = now; - } + long now = System.nanoTime(); + nanosTimeout -= now - lastTime; + lastTime = now; + if (Thread.interrupted()) + throw new InterruptedException(); } - } catch (RuntimeException ex) { - cancelAcquire(node); - throw ex; + } finally { + if (failed) + cancelAcquire(node); } - // Arrive here only if interrupted - cancelAcquire(node); - throw new InterruptedException(); } - // Main exported methods + // Main exported methods /** * Attempts to acquire in exclusive mode. This method should query @@ -901,22 +1010,21 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * acquire. If this method reports failure, the acquire method * may queue the thread, if it is not already queued, until it is * signalled by a release from some other thread. This can be used - * to implement method {@link Lock#tryLock()}. + * to implement method {@link Lock#tryLock()}. * * <p>The default - * implementation throws {@link UnsupportedOperationException} + * implementation throws {@link UnsupportedOperationException}. * - * @param arg the acquire argument. This value - * is always the one passed to an acquire method, - * or is the value saved on entry to a condition wait. - * The value is otherwise uninterpreted and can represent anything - * you like. - * @return true if successful. Upon success, this object has been - * acquired. - * @throws IllegalMonitorStateException if acquiring would place - * this synchronizer in an illegal state. This exception must be - * thrown in a consistent fashion for synchronization to work - * correctly. + * @param arg the acquire argument. This value is always the one + * passed to an acquire method, or is the value saved on entry + * to a condition wait. The value is otherwise uninterpreted + * and can represent anything you like. + * @return {@code true} if successful. Upon success, this object has + * been acquired. + * @throws IllegalMonitorStateException if acquiring would place this + * synchronizer in an illegal state. This exception must be + * thrown in a consistent fashion for synchronization to work + * correctly. * @throws UnsupportedOperationException if exclusive mode is not supported */ protected boolean tryAcquire(int arg) { @@ -925,23 +1033,24 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable /** * Attempts to set the state to reflect a release in exclusive - * mode. <p>This method is always invoked by the thread - * performing release. + * mode. + * + * <p>This method is always invoked by the thread performing release. * * <p>The default implementation throws - * {@link UnsupportedOperationException} - * @param arg the release argument. This value - * is always the one passed to a release method, - * or the current state value upon entry to a condition wait. - * The value is otherwise uninterpreted and can represent anything - * you like. - * @return <tt>true</tt> if this object is now in a fully released state, - * so that any waiting threads may attempt to acquire; and <tt>false</tt> - * otherwise. - * @throws IllegalMonitorStateException if releasing would place - * this synchronizer in an illegal state. This exception must be - * thrown in a consistent fashion for synchronization to work - * correctly. + * {@link UnsupportedOperationException}. + * + * @param arg the release argument. This value is always the one + * passed to a release method, or the current state value upon + * entry to a condition wait. The value is otherwise + * uninterpreted and can represent anything you like. + * @return {@code true} if this object is now in a fully released + * state, so that any waiting threads may attempt to acquire; + * and {@code false} otherwise. + * @throws IllegalMonitorStateException if releasing would place this + * synchronizer in an illegal state. This exception must be + * thrown in a consistent fashion for synchronization to work + * correctly. * @throws UnsupportedOperationException if exclusive mode is not supported */ protected boolean tryRelease(int arg) { @@ -951,7 +1060,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable /** * Attempts to acquire in shared mode. This method should query if * the state of the object permits it to be acquired in the shared - * mode, and if so to acquire it. + * mode, and if so to acquire it. * * <p>This method is always invoked by the thread performing * acquire. If this method reports failure, the acquire method @@ -959,24 +1068,25 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * signalled by a release from some other thread. * * <p>The default implementation throws {@link - * UnsupportedOperationException} + * UnsupportedOperationException}. * - * @param arg the acquire argument. This value - * is always the one passed to an acquire method, - * or is the value saved on entry to a condition wait. - * The value is otherwise uninterpreted and can represent anything - * you like. - * @return a negative value on failure, zero on exclusive success, - * and a positive value if non-exclusively successful, in which - * case a subsequent waiting thread must check - * availability. (Support for three different return values - * enables this method to be used in contexts where acquires only - * sometimes act exclusively.) Upon success, this object has been - * acquired. - * @throws IllegalMonitorStateException if acquiring would place - * this synchronizer in an illegal state. This exception must be - * thrown in a consistent fashion for synchronization to work - * correctly. + * @param arg the acquire argument. This value is always the one + * passed to an acquire method, or is the value saved on entry + * to a condition wait. The value is otherwise uninterpreted + * and can represent anything you like. + * @return a negative value on failure; zero if acquisition in shared + * mode succeeded but no subsequent shared-mode acquire can + * succeed; and a positive value if acquisition in shared + * mode succeeded and subsequent shared-mode acquires might + * also succeed, in which case a subsequent waiting thread + * must check availability. (Support for three different + * return values enables this method to be used in contexts + * where acquires only sometimes act exclusively.) Upon + * success, this object has been acquired. + * @throws IllegalMonitorStateException if acquiring would place this + * synchronizer in an illegal state. This exception must be + * thrown in a consistent fashion for synchronization to work + * correctly. * @throws UnsupportedOperationException if shared mode is not supported */ protected int tryAcquireShared(int arg) { @@ -985,21 +1095,23 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable /** * Attempts to set the state to reflect a release in shared mode. + * * <p>This method is always invoked by the thread performing release. - * <p> The default implementation throws - * {@link UnsupportedOperationException} - * @param arg the release argument. This value - * is always the one passed to a release method, - * or the current state value upon entry to a condition wait. - * The value is otherwise uninterpreted and can represent anything - * you like. - * @return <tt>true</tt> if this object is now in a fully released state, - * so that any waiting threads may attempt to acquire; and <tt>false</tt> - * otherwise. - * @throws IllegalMonitorStateException if releasing would place - * this synchronizer in an illegal state. This exception must be - * thrown in a consistent fashion for synchronization to work - * correctly. + * + * <p>The default implementation throws + * {@link UnsupportedOperationException}. + * + * @param arg the release argument. This value is always the one + * passed to a release method, or the current state value upon + * entry to a condition wait. The value is otherwise + * uninterpreted and can represent anything you like. + * @return {@code true} if this release of shared mode may permit a + * waiting acquire (shared or exclusive) to succeed; and + * {@code false} otherwise + * @throws IllegalMonitorStateException if releasing would place this + * synchronizer in an illegal state. This exception must be + * thrown in a consistent fashion for synchronization to work + * correctly. * @throws UnsupportedOperationException if shared mode is not supported */ protected boolean tryReleaseShared(int arg) { @@ -1007,17 +1119,18 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable } /** - * Returns true if synchronization is held exclusively with respect - * to the current (calling) thread. This method is invoked + * Returns {@code true} if synchronization is held exclusively with + * respect to the current (calling) thread. This method is invoked * upon each call to a non-waiting {@link ConditionObject} method. * (Waiting methods instead invoke {@link #release}.) + * * <p>The default implementation throws {@link * UnsupportedOperationException}. This method is invoked * internally only within {@link ConditionObject} methods, so need * not be defined if conditions are not used. * - * @return true if synchronization is held exclusively; - * else false + * @return {@code true} if synchronization is held exclusively; + * {@code false} otherwise * @throws UnsupportedOperationException if conditions are not supported */ protected boolean isHeldExclusively() { @@ -1030,12 +1143,12 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * returning on success. Otherwise the thread is queued, possibly * repeatedly blocking and unblocking, invoking {@link * #tryAcquire} until success. This method can be used - * to implement method {@link Lock#lock} - * @param arg the acquire argument. - * This value is conveyed to {@link #tryAcquire} but is - * otherwise uninterpreted and can represent anything - * you like. - */ + * to implement method {@link Lock#lock}. + * + * @param arg the acquire argument. This value is conveyed to + * {@link #tryAcquire} but is otherwise uninterpreted and + * can represent anything you like. + */ public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) @@ -1049,11 +1162,11 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * success. Otherwise the thread is queued, possibly repeatedly * blocking and unblocking, invoking {@link #tryAcquire} * until success or the thread is interrupted. This method can be - * used to implement method {@link Lock#lockInterruptibly} - * @param arg the acquire argument. - * This value is conveyed to {@link #tryAcquire} but is - * otherwise uninterpreted and can represent anything - * you like. + * used to implement method {@link Lock#lockInterruptibly}. + * + * @param arg the acquire argument. This value is conveyed to + * {@link #tryAcquire} but is otherwise uninterpreted and + * can represent anything you like. * @throws InterruptedException if the current thread is interrupted */ public final void acquireInterruptibly(int arg) throws InterruptedException { @@ -1072,35 +1185,35 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * {@link #tryAcquire} until success or the thread is interrupted * or the timeout elapses. This method can be used to implement * method {@link Lock#tryLock(long, TimeUnit)}. - * @param arg the acquire argument. - * This value is conveyed to {@link #tryAcquire} but is - * otherwise uninterpreted and can represent anything - * you like. + * + * @param arg the acquire argument. This value is conveyed to + * {@link #tryAcquire} but is otherwise uninterpreted and + * can represent anything you like. * @param nanosTimeout the maximum number of nanoseconds to wait - * @return true if acquired; false if timed out + * @return {@code true} if acquired; {@code false} if timed out * @throws InterruptedException if the current thread is interrupted */ - public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { - if (Thread.interrupted()) - throw new InterruptedException(); - return tryAcquire(arg) || - doAcquireNanos(arg, nanosTimeout); - } + public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { + if (Thread.interrupted()) + throw new InterruptedException(); + return tryAcquire(arg) || + doAcquireNanos(arg, nanosTimeout); + } /** * Releases in exclusive mode. Implemented by unblocking one or * more threads if {@link #tryRelease} returns true. - * This method can be used to implement method {@link Lock#unlock} - * @param arg the release argument. - * This value is conveyed to {@link #tryRelease} but is - * otherwise uninterpreted and can represent anything - * you like. - * @return the value returned from {@link #tryRelease} + * This method can be used to implement method {@link Lock#unlock}. + * + * @param arg the release argument. This value is conveyed to + * {@link #tryRelease} but is otherwise uninterpreted and + * can represent anything you like. + * @return the value returned from {@link #tryRelease} */ public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; - if (h != null && h.waitStatus != 0) + if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } @@ -1112,11 +1225,11 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * first invoking at least once {@link #tryAcquireShared}, * returning on success. Otherwise the thread is queued, possibly * repeatedly blocking and unblocking, invoking {@link - * #tryAcquireShared} until success. - * @param arg the acquire argument. - * This value is conveyed to {@link #tryAcquireShared} but is - * otherwise uninterpreted and can represent anything - * you like. + * #tryAcquireShared} until success. + * + * @param arg the acquire argument. This value is conveyed to + * {@link #tryAcquireShared} but is otherwise uninterpreted + * and can represent anything you like. */ public final void acquireShared(int arg) { if (tryAcquireShared(arg) < 0) @@ -1129,8 +1242,8 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * {@link #tryAcquireShared}, returning on success. Otherwise the * thread is queued, possibly repeatedly blocking and unblocking, * invoking {@link #tryAcquireShared} until success or the thread - * is interrupted. - * @param arg the acquire argument. + * is interrupted. + * @param arg the acquire argument * This value is conveyed to {@link #tryAcquireShared} but is * otherwise uninterpreted and can represent anything * you like. @@ -1141,7 +1254,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); - } + } /** * Attempts to acquire in shared mode, aborting if interrupted, and @@ -1151,35 +1264,33 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * thread is queued, possibly repeatedly blocking and unblocking, * invoking {@link #tryAcquireShared} until success or the thread * is interrupted or the timeout elapses. - * @param arg the acquire argument. - * This value is conveyed to {@link #tryAcquireShared} but is - * otherwise uninterpreted and can represent anything - * you like. + * + * @param arg the acquire argument. This value is conveyed to + * {@link #tryAcquireShared} but is otherwise uninterpreted + * and can represent anything you like. * @param nanosTimeout the maximum number of nanoseconds to wait - * @return true if acquired; false if timed out + * @return {@code true} if acquired; {@code false} if timed out * @throws InterruptedException if the current thread is interrupted */ - public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException { - if (Thread.interrupted()) - throw new InterruptedException(); - return tryAcquireShared(arg) >= 0 || - doAcquireSharedNanos(arg, nanosTimeout); - } + public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException { + if (Thread.interrupted()) + throw new InterruptedException(); + return tryAcquireShared(arg) >= 0 || + doAcquireSharedNanos(arg, nanosTimeout); + } /** * Releases in shared mode. Implemented by unblocking one or more - * threads if {@link #tryReleaseShared} returns true. - * @param arg the release argument. - * This value is conveyed to {@link #tryReleaseShared} but is - * otherwise uninterpreted and can represent anything - * you like. - * @return the value returned from {@link #tryReleaseShared} + * threads if {@link #tryReleaseShared} returns true. + * + * @param arg the release argument. This value is conveyed to + * {@link #tryReleaseShared} but is otherwise uninterpreted + * and can represent anything you like. + * @return the value returned from {@link #tryReleaseShared} */ public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { - Node h = head; - if (h != null && h.waitStatus != 0) - unparkSuccessor(h); + doReleaseShared(); return true; } return false; @@ -1190,16 +1301,15 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable /** * Queries whether any threads are waiting to acquire. Note that * because cancellations due to interrupts and timeouts may occur - * at any time, a <tt>true</tt> return does not guarantee that any + * at any time, a {@code true} return does not guarantee that any * other thread will ever acquire. * - * <p> In this implementation, this operation returns in + * <p>In this implementation, this operation returns in * constant time. * - * @return true if there may be other threads waiting to acquire - * the lock. + * @return {@code true} if there may be other threads waiting to acquire */ - public final boolean hasQueuedThreads() { + public final boolean hasQueuedThreads() { return head != tail; } @@ -1207,10 +1317,10 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * Queries whether any threads have ever contended to acquire this * synchronizer; that is if an acquire method has ever blocked. * - * <p> In this implementation, this operation returns in + * <p>In this implementation, this operation returns in * constant time. * - * @return true if there has ever been contention + * @return {@code true} if there has ever been contention */ public final boolean hasContended() { return head != null; @@ -1218,18 +1328,18 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable /** * Returns the first (longest-waiting) thread in the queue, or - * <tt>null</tt> if no threads are currently queued. + * {@code null} if no threads are currently queued. * - * <p> In this implementation, this operation normally returns in + * <p>In this implementation, this operation normally returns in * constant time, but may iterate upon contention if other threads are * concurrently modifying the queue. * * @return the first (longest-waiting) thread in the queue, or - * <tt>null</tt> if no threads are currently queued. + * {@code null} if no threads are currently queued */ public final Thread getFirstQueuedThread() { // handle only fast path, else relay - return (head == tail)? null : fullGetFirstQueuedThread(); + return (head == tail) ? null : fullGetFirstQueuedThread(); } /** @@ -1237,57 +1347,49 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable */ private Thread fullGetFirstQueuedThread() { /* - * This loops only if the queue changes while we read sets of - * fields. + * The first node is normally head.next. Try to get its + * thread field, ensuring consistent reads: If thread + * field is nulled out or s.prev is no longer head, then + * some other thread(s) concurrently performed setHead in + * between some of our reads. We try this twice before + * resorting to traversal. */ - for (;;) { - Node h = head; - if (h == null) // No queue - return null; + Node h, s; + Thread st; + if (((h = head) != null && (s = h.next) != null && + s.prev == head && (st = s.thread) != null) || + ((h = head) != null && (s = h.next) != null && + s.prev == head && (st = s.thread) != null)) + return st; - /* - * The first node is normally h.next. Try to get its - * thread field, ensuring consistent reads: If thread - * field is nulled out or s.prev is no longer head, then - * some other thread(s) concurrently performed setHead in - * between some of our reads, so we must reread. - */ - Node s = h.next; - if (s != null) { - Thread st = s.thread; - Node sp = s.prev; - if (st != null && sp == head) - return st; - } + /* + * Head's next field might not have been set yet, or may have + * been unset after setHead. So we must check to see if tail + * is actually first node. If not, we continue on, safely + * traversing from tail back to head to find first, + * guaranteeing termination. + */ - /* - * Head's next field might not have been set yet, or may - * have been unset after setHead. So we must check to see - * if tail is actually first node, in almost the same way - * as above. - */ - Node t = tail; - if (t == h) // Empty queue - return null; - - if (t != null) { - Thread tt = t.thread; - Node tp = t.prev; - if (tt != null && tp == head) - return tt; - } + Node t = tail; + Thread firstThread = null; + while (t != null && t != head) { + Thread tt = t.thread; + if (tt != null) + firstThread = tt; + t = t.prev; } + return firstThread; } /** - * Returns true if the given thread is currently queued. + * Returns true if the given thread is currently queued. * - * <p> This implementation traverses the queue to determine + * <p>This implementation traverses the queue to determine * presence of the given thread. * * @param thread the thread - * @return true if the given thread in on the queue - * @throws NullPointerException if thread null + * @return {@code true} if the given thread is on the queue + * @throws NullPointerException if the thread is null */ public final boolean isQueued(Thread thread) { if (thread == null) @@ -1298,6 +1400,77 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable return false; } + /** + * Returns {@code true} if the apparent first queued thread, if one + * exists, is waiting in exclusive mode. If this method returns + * {@code true}, and the current thread is attempting to acquire in + * shared mode (that is, this method is invoked from {@link + * #tryAcquireShared}) then it is guaranteed that the current thread + * is not the first queued thread. Used only as a heuristic in + * ReentrantReadWriteLock. + */ + final boolean apparentlyFirstQueuedIsExclusive() { + Node h, s; + return (h = head) != null && + (s = h.next) != null && + !s.isShared() && + s.thread != null; + } + + /** + * Queries whether any threads have been waiting to acquire longer + * than the current thread. + * + * <p>An invocation of this method is equivalent to (but may be + * more efficient than): + * <pre> {@code + * getFirstQueuedThread() != Thread.currentThread() && + * hasQueuedThreads()}</pre> + * + * <p>Note that because cancellations due to interrupts and + * timeouts may occur at any time, a {@code true} return does not + * guarantee that some other thread will acquire before the current + * thread. Likewise, it is possible for another thread to win a + * race to enqueue after this method has returned {@code false}, + * due to the queue being empty. + * + * <p>This method is designed to be used by a fair synchronizer to + * avoid <a href="AbstractQueuedSynchronizer#barging">barging</a>. + * Such a synchronizer's {@link #tryAcquire} method should return + * {@code false}, and its {@link #tryAcquireShared} method should + * return a negative value, if this method returns {@code true} + * (unless this is a reentrant acquire). For example, the {@code + * tryAcquire} method for a fair, reentrant, exclusive mode + * synchronizer might look like this: + * + * <pre> {@code + * protected boolean tryAcquire(int arg) { + * if (isHeldExclusively()) { + * // A reentrant acquire; increment hold count + * return true; + * } else if (hasQueuedPredecessors()) { + * return false; + * } else { + * // try to acquire normally + * } + * }}</pre> + * + * @return {@code true} if there is a queued thread preceding the + * current thread, and {@code false} if the current thread + * is at the head of the queue or the queue is empty + */ + final boolean hasQueuedPredecessors() { + // The correctness of this depends on head being initialized + // before tail and on head.next being accurate if the current + // thread is first in queue. + Node t = tail; // Read fields in reverse initialization order + Node h = head; + Node s; + return h != t && + ((s = h.next) == null || s.thread != Thread.currentThread()); + } + + // Instrumentation and monitoring methods /** @@ -1308,7 +1481,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * monitoring system state, not for synchronization * control. * - * @return the estimated number of threads waiting for this lock + * @return the estimated number of threads waiting to acquire */ public final int getQueueLength() { int n = 0; @@ -1327,6 +1500,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * returned collection are in no particular order. This method is * designed to facilitate construction of subclasses that provide * more extensive monitoring facilities. + * * @return the collection of threads */ public final Collection<Thread> getQueuedThreads() { @@ -1344,6 +1518,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * acquire in exclusive mode. This has the same properties * as {@link #getQueuedThreads} except that it only returns * those threads waiting due to an exclusive acquire. + * * @return the collection of threads */ public final Collection<Thread> getExclusiveQueuedThreads() { @@ -1363,6 +1538,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * acquire in shared mode. This has the same properties * as {@link #getQueuedThreads} except that it only returns * those threads waiting due to a shared acquire. + * * @return the collection of threads */ public final Collection<Thread> getSharedQueuedThreads() { @@ -1378,18 +1554,18 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable } /** - * Returns a string identifying this synchronizer, as well as its - * state. The state, in brackets, includes the String "State - * =" followed by the current value of {@link #getState}, and - * either "nonempty" or "empty" depending on - * whether the queue is empty. + * Returns a string identifying this synchronizer, as well as its state. + * The state, in brackets, includes the String {@code "State ="} + * followed by the current value of {@link #getState}, and either + * {@code "nonempty"} or {@code "empty"} depending on whether the + * queue is empty. * - * @return a string identifying this synchronizer, as well as its state. + * @return a string identifying this synchronizer, as well as its state */ public String toString() { int s = getState(); - String q = hasQueuedThreads()? "non" : ""; - return super.toString() + + String q = hasQueuedThreads() ? "non" : ""; + return super.toString() + "[State = " + s + ", " + q + "empty queue]"; } @@ -1416,7 +1592,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * there, so we hardly ever traverse much. */ return findNodeFromTail(node); - } + } /** * Returns true if node is on sync queue by searching backwards from tail. @@ -1424,7 +1600,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * @return true if present */ private boolean findNodeFromTail(Node node) { - Node t = tail; + Node t = tail; for (;;) { if (t == node) return true; @@ -1435,7 +1611,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable } /** - * Transfers a node from a condition queue onto sync queue. + * Transfers a node from a condition queue onto sync queue. * Returns true if successful. * @param node the node * @return true if successfully transferred (else the node was @@ -1455,8 +1631,8 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * case the waitStatus can be transiently and harmlessly wrong). */ Node p = enq(node); - int c = p.waitStatus; - if (c > 0 || !compareAndSetWaitStatus(p, c, Node.SIGNAL)) + int ws = p.waitStatus; + if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) LockSupport.unpark(node.thread); return true; } @@ -1465,9 +1641,8 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * Transfers node, if necessary, to sync queue after a cancelled * wait. Returns true if thread was cancelled before being * signalled. - * @param current the waiting thread * @param node its node - * @return true if cancelled before the node was signalled. + * @return true if cancelled before the node was signalled */ final boolean transferAfterCancelledWait(Node node) { if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) { @@ -1480,39 +1655,42 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * incomplete transfer is both rare and transient, so just * spin. */ - while (!isOnSyncQueue(node)) + while (!isOnSyncQueue(node)) Thread.yield(); return false; } /** - * Invoke release with current state value; return saved state. - * Cancel node and throw exception on failure. + * Invokes release with current state value; returns saved state. + * Cancels node and throws exception on failure. * @param node the condition node for this wait * @return previous sync state */ final int fullyRelease(Node node) { + boolean failed = true; try { int savedState = getState(); - if (release(savedState)) + if (release(savedState)) { + failed = false; return savedState; - } catch(RuntimeException ex) { - node.waitStatus = Node.CANCELLED; - throw ex; + } else { + throw new IllegalMonitorStateException(); + } + } finally { + if (failed) + node.waitStatus = Node.CANCELLED; } - // reach here if release fails - node.waitStatus = Node.CANCELLED; - throw new IllegalMonitorStateException(); } // Instrumentation methods for conditions /** - * Queries whether the given ConditionObject + * Queries whether the given ConditionObject * uses this synchronizer as its lock. + * * @param condition the condition * @return <tt>true</tt> if owned - * @throws NullPointerException if condition null + * @throws NullPointerException if the condition is null */ public final boolean owns(ConditionObject condition) { if (condition == null) @@ -1527,14 +1705,15 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * does not guarantee that a future <tt>signal</tt> will awaken * any threads. This method is designed primarily for use in * monitoring of the system state. + * * @param condition the condition - * @return <tt>true</tt> if there are any waiting threads. - * @throws IllegalMonitorStateException if exclusive synchronization - * is not held + * @return <tt>true</tt> if there are any waiting threads + * @throws IllegalMonitorStateException if exclusive synchronization + * is not held * @throws IllegalArgumentException if the given condition is - * not associated with this synchronizer - * @throws NullPointerException if condition null - */ + * not associated with this synchronizer + * @throws NullPointerException if the condition is null + */ public final boolean hasWaiters(ConditionObject condition) { if (!owns(condition)) throw new IllegalArgumentException("Not owner"); @@ -1548,14 +1727,15 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * estimate serves only as an upper bound on the actual number of * waiters. This method is designed for use in monitoring of the * system state, not for synchronization control. + * * @param condition the condition - * @return the estimated number of waiting threads. - * @throws IllegalMonitorStateException if exclusive synchronization - * is not held + * @return the estimated number of waiting threads + * @throws IllegalMonitorStateException if exclusive synchronization + * is not held * @throws IllegalArgumentException if the given condition is - * not associated with this synchronizer - * @throws NullPointerException if condition null - */ + * not associated with this synchronizer + * @throws NullPointerException if the condition is null + */ public final int getWaitQueueLength(ConditionObject condition) { if (!owns(condition)) throw new IllegalArgumentException("Not owner"); @@ -1568,14 +1748,15 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * synchronizer. Because the actual set of threads may change * dynamically while constructing this result, the returned * collection is only a best-effort estimate. The elements of the - * returned collection are in no particular order. + * returned collection are in no particular order. + * * @param condition the condition * @return the collection of threads - * @throws IllegalMonitorStateException if exclusive synchronization - * is not held + * @throws IllegalMonitorStateException if exclusive synchronization + * is not held * @throws IllegalArgumentException if the given condition is - * not associated with this synchronizer - * @throws NullPointerException if condition null + * not associated with this synchronizer + * @throws NullPointerException if the condition is null */ public final Collection<Thread> getWaitingThreads(ConditionObject condition) { if (!owns(condition)) @@ -1588,14 +1769,14 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * AbstractQueuedSynchronizer} serving as the basis of a {@link * Lock} implementation. * - * <p> Method documentation for this class describes mechanics, + * <p>Method documentation for this class describes mechanics, * not behavioral specifications from the point of view of Lock * and Condition users. Exported versions of this class will in * general need to be accompanied by documentation describing * condition semantics that rely on those of the associated * <tt>AbstractQueuedSynchronizer</tt>. - * - * <p> This class is Serializable, but all fields are transient, + * + * <p>This class is Serializable, but all fields are transient, * so deserialized conditions have no waiters. */ public class ConditionObject implements Condition, java.io.Serializable { @@ -1613,29 +1794,34 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable // Internal methods /** - * Add a new waiter to wait queue + * Adds a new waiter to wait queue. * @return its new wait node */ private Node addConditionWaiter() { - Node node = new Node(Thread.currentThread(), Node.CONDITION); Node t = lastWaiter; - if (t == null) + // If lastWaiter is cancelled, clean out. + if (t != null && t.waitStatus != Node.CONDITION) { + unlinkCancelledWaiters(); + t = lastWaiter; + } + Node node = new Node(Thread.currentThread(), Node.CONDITION); + if (t == null) firstWaiter = node; - else + else t.nextWaiter = node; lastWaiter = node; return node; } /** - * Remove and transfer nodes until hit non-cancelled one or + * Removes and transfers nodes until hit non-cancelled one or * null. Split out from signal in part to encourage compilers * to inline the case of no waiters. * @param first (non-null) the first node on condition queue */ private void doSignal(Node first) { do { - if ( (firstWaiter = first.nextWaiter) == null) + if ( (firstWaiter = first.nextWaiter) == null) lastWaiter = null; first.nextWaiter = null; } while (!transferForSignal(first) && @@ -1643,11 +1829,11 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable } /** - * Remove and transfer all nodes. + * Removes and transfers all nodes. * @param first (non-null) the first node on condition queue */ private void doSignalAll(Node first) { - lastWaiter = firstWaiter = null; + lastWaiter = firstWaiter = null; do { Node next = first.nextWaiter; first.nextWaiter = null; @@ -1656,45 +1842,81 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable } while (first != null); } + /** + * Unlinks cancelled waiter nodes from condition queue. + * Called only while holding lock. This is called when + * cancellation occurred during condition wait, and upon + * insertion of a new waiter when lastWaiter is seen to have + * been cancelled. This method is needed to avoid garbage + * retention in the absence of signals. So even though it may + * require a full traversal, it comes into play only when + * timeouts or cancellations occur in the absence of + * signals. It traverses all nodes rather than stopping at a + * particular target to unlink all pointers to garbage nodes + * without requiring many re-traversals during cancellation + * storms. + */ + private void unlinkCancelledWaiters() { + Node t = firstWaiter; + Node trail = null; + while (t != null) { + Node next = t.nextWaiter; + if (t.waitStatus != Node.CONDITION) { + t.nextWaiter = null; + if (trail == null) + firstWaiter = next; + else + trail.nextWaiter = next; + if (next == null) + lastWaiter = trail; + } + else + trail = t; + t = next; + } + } + // public methods /** * Moves the longest-waiting thread, if one exists, from the * wait queue for this condition to the wait queue for the * owning lock. + * * @throws IllegalMonitorStateException if {@link #isHeldExclusively} - * returns false + * returns {@code false} */ public final void signal() { - if (!isHeldExclusively()) + if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null) doSignal(first); } - + /** * Moves all threads from the wait queue for this condition to * the wait queue for the owning lock. + * * @throws IllegalMonitorStateException if {@link #isHeldExclusively} - * returns false + * returns {@code false} */ public final void signalAll() { - if (!isHeldExclusively()) + if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; - if (first != null) + if (first != null) doSignalAll(first); } /** * Implements uninterruptible condition wait. * <ol> - * <li> Save lock state returned by {@link #getState} - * <li> Invoke {@link #release} with - * saved state as argument, throwing - * IllegalMonitorStateException if it fails. - * <li> Block until signalled + * <li> Save lock state returned by {@link #getState}. + * <li> Invoke {@link #release} with + * saved state as argument, throwing + * IllegalMonitorStateException if it fails. + * <li> Block until signalled. * <li> Reacquire by invoking specialized version of * {@link #acquire} with saved state as argument. * </ol> @@ -1705,7 +1927,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable boolean interrupted = false; while (!isOnSyncQueue(node)) { LockSupport.park(); - if (Thread.interrupted()) + if (Thread.interrupted()) interrupted = true; } if (acquireQueued(node, savedState) || interrupted) @@ -1725,47 +1947,47 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable private static final int THROW_IE = -1; /** - * Check for interrupt, returning THROW_IE if interrupted + * Checks for interrupt, returning THROW_IE if interrupted * before signalled, REINTERRUPT if after signalled, or * 0 if not interrupted. */ private int checkInterruptWhileWaiting(Node node) { - return (Thread.interrupted()) ? - ((transferAfterCancelledWait(node))? THROW_IE : REINTERRUPT) : + return Thread.interrupted() ? + (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) : 0; } /** - * Throw InterruptedException, reinterrupt current thread, or - * do nothing, depending on mode. + * Throws InterruptedException, reinterrupts current thread, or + * does nothing, depending on mode. */ - private void reportInterruptAfterWait(int interruptMode) + private void reportInterruptAfterWait(int interruptMode) throws InterruptedException { if (interruptMode == THROW_IE) throw new InterruptedException(); else if (interruptMode == REINTERRUPT) - Thread.currentThread().interrupt(); + selfInterrupt(); } /** * Implements interruptible condition wait. * <ol> - * <li> If current thread is interrupted, throw InterruptedException - * <li> Save lock state returned by {@link #getState} - * <li> Invoke {@link #release} with - * saved state as argument, throwing - * IllegalMonitorStateException if it fails. - * <li> Block until signalled or interrupted + * <li> If current thread is interrupted, throw InterruptedException. + * <li> Save lock state returned by {@link #getState}. + * <li> Invoke {@link #release} with + * saved state as argument, throwing + * IllegalMonitorStateException if it fails. + * <li> Block until signalled or interrupted. * <li> Reacquire by invoking specialized version of * {@link #acquire} with saved state as argument. - * <li> If interrupted while blocked in step 4, throw exception + * <li> If interrupted while blocked in step 4, throw InterruptedException. * </ol> - * + * * @throws InterruptedException if the current thread is interrupted (and * interruption of thread suspension is supported). */ public final void await() throws InterruptedException { - if (Thread.interrupted()) + if (Thread.interrupted()) throw new InterruptedException(); Node node = addConditionWaiter(); int savedState = fullyRelease(node); @@ -1777,6 +1999,8 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable } if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; + if (node.nextWaiter != null) // clean up if cancelled + unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); } @@ -1784,17 +2008,17 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable /** * Implements timed condition wait. * <ol> - * <li> If current thread is interrupted, throw InterruptedException - * <li> Save lock state returned by {@link #getState} - * <li> Invoke {@link #release} with - * saved state as argument, throwing - * IllegalMonitorStateException if it fails. - * <li> Block until signalled, interrupted, or timed out + * <li> If current thread is interrupted, throw InterruptedException. + * <li> Save lock state returned by {@link #getState}. + * <li> Invoke {@link #release} with + * saved state as argument, throwing + * IllegalMonitorStateException if it fails. + * <li> Block until signalled, interrupted, or timed out. * <li> Reacquire by invoking specialized version of * {@link #acquire} with saved state as argument. - * <li> If interrupted while blocked in step 4, throw InterruptedException + * <li> If interrupted while blocked in step 4, throw InterruptedException. * </ol> - * + * * @param nanosTimeout the maximum time to wait, in nanoseconds * @return A value less than or equal to zero if the wait has * timed out; otherwise an estimate, that @@ -1805,7 +2029,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable * interruption of thread suspension is supported). */ public final long awaitNanos(long nanosTimeout) throws InterruptedException { - if (Thread.interrupted()) + if (Thread.interrupted()) throw new InterruptedException(); Node node = addConditionWaiter(); int savedState = fullyRelease(node); @@ -1813,18 +2037,21 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable int interruptMode = 0; while (!isOnSyncQueue(node)) { if (nanosTimeout <= 0L) { - transferAfterCancelledWait(node); + transferAfterCancelledWait(node); break; } LockSupport.parkNanos(nanosTimeout); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; + long now = System.nanoTime(); nanosTimeout -= now - lastTime; lastTime = now; } if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; + if (node.nextWaiter != null) + unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); return nanosTimeout - (System.nanoTime() - lastTime); @@ -1833,18 +2060,18 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable /** * Implements absolute timed condition wait. * <ol> - * <li> If current thread is interrupted, throw InterruptedException - * <li> Save lock state returned by {@link #getState} - * <li> Invoke {@link #release} with - * saved state as argument, throwing - * IllegalMonitorStateException if it fails. - * <li> Block until signalled, interrupted, or timed out + * <li> If current thread is interrupted, throw InterruptedException. + * <li> Save lock state returned by {@link #getState}. + * <li> Invoke {@link #release} with + * saved state as argument, throwing + * IllegalMonitorStateException if it fails. + * <li> Block until signalled, interrupted, or timed out. * <li> Reacquire by invoking specialized version of * {@link #acquire} with saved state as argument. - * <li> If interrupted while blocked in step 4, throw InterruptedException - * <li> If timed out while blocked in step 4, return false, else true + * <li> If interrupted while blocked in step 4, throw InterruptedException. + * <li> If timed out while blocked in step 4, return false, else true. * </ol> - * + * * @param deadline the absolute time to wait until * @return <tt>false</tt> if the deadline has * elapsed upon return, else <tt>true</tt>. @@ -1856,7 +2083,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable if (deadline == null) throw new NullPointerException(); long abstime = deadline.getTime(); - if (Thread.interrupted()) + if (Thread.interrupted()) throw new InterruptedException(); Node node = addConditionWaiter(); int savedState = fullyRelease(node); @@ -1864,7 +2091,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable int interruptMode = 0; while (!isOnSyncQueue(node)) { if (System.currentTimeMillis() > abstime) { - timedout = transferAfterCancelledWait(node); + timedout = transferAfterCancelledWait(node); break; } LockSupport.parkUntil(abstime); @@ -1873,26 +2100,28 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable } if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; + if (node.nextWaiter != null) + unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); return !timedout; } - + /** - * Implements timed condition wait. + * Implements timed condition wait. * <ol> - * <li> If current thread is interrupted, throw InterruptedException - * <li> Save lock state returned by {@link #getState} - * <li> Invoke {@link #release} with - * saved state as argument, throwing - * IllegalMonitorStateException if it fails. - * <li> Block until signalled, interrupted, or timed out + * <li> If current thread is interrupted, throw InterruptedException. + * <li> Save lock state returned by {@link #getState}. + * <li> Invoke {@link #release} with + * saved state as argument, throwing + * IllegalMonitorStateException if it fails. + * <li> Block until signalled, interrupted, or timed out. * <li> Reacquire by invoking specialized version of * {@link #acquire} with saved state as argument. - * <li> If interrupted while blocked in step 4, throw InterruptedException - * <li> If timed out while blocked in step 4, return false, else true + * <li> If interrupted while blocked in step 4, throw InterruptedException. + * <li> If timed out while blocked in step 4, return false, else true. * </ol> - * + * * @param time the maximum time to wait * @param unit the time unit of the <tt>time</tt> argument. * @return <tt>false</tt> if the waiting time detectably elapsed @@ -1904,7 +2133,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable if (unit == null) throw new NullPointerException(); long nanosTimeout = unit.toNanos(time); - if (Thread.interrupted()) + if (Thread.interrupted()) throw new InterruptedException(); Node node = addConditionWaiter(); int savedState = fullyRelease(node); @@ -1913,10 +2142,11 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable int interruptMode = 0; while (!isOnSyncQueue(node)) { if (nanosTimeout <= 0L) { - timedout = transferAfterCancelledWait(node); + timedout = transferAfterCancelledWait(node); break; } - LockSupport.parkNanos(nanosTimeout); + if (nanosTimeout >= spinForTimeoutThreshold) + LockSupport.parkNanos(nanosTimeout); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; long now = System.nanoTime(); @@ -1925,6 +2155,8 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable } if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; + if (node.nextWaiter != null) + unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); return !timedout; @@ -1934,8 +2166,9 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable /** * Returns true if this condition was created by the given - * synchronization object - * @return true if owned + * synchronization object. + * + * @return {@code true} if owned */ final boolean isOwnedBy(AbstractQueuedSynchronizer sync) { return sync == AbstractQueuedSynchronizer.this; @@ -1943,13 +2176,14 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable /** * Queries whether any threads are waiting on this condition. - * Implements {@link AbstractQueuedSynchronizer#hasWaiters} - * @return <tt>true</tt> if there are any waiting threads. + * Implements {@link AbstractQueuedSynchronizer#hasWaiters}. + * + * @return {@code true} if there are any waiting threads * @throws IllegalMonitorStateException if {@link #isHeldExclusively} - * returns false - */ + * returns {@code false} + */ protected final boolean hasWaiters() { - if (!isHeldExclusively()) + if (!isHeldExclusively()) throw new IllegalMonitorStateException(); for (Node w = firstWaiter; w != null; w = w.nextWaiter) { if (w.waitStatus == Node.CONDITION) @@ -1960,14 +2194,15 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable /** * Returns an estimate of the number of threads waiting on - * this condition. - * Implements {@link AbstractQueuedSynchronizer#getWaitQueueLength} - * @return the estimated number of waiting threads. + * this condition. + * Implements {@link AbstractQueuedSynchronizer#getWaitQueueLength}. + * + * @return the estimated number of waiting threads * @throws IllegalMonitorStateException if {@link #isHeldExclusively} - * returns false - */ + * returns {@code false} + */ protected final int getWaitQueueLength() { - if (!isHeldExclusively()) + if (!isHeldExclusively()) throw new IllegalMonitorStateException(); int n = 0; for (Node w = firstWaiter; w != null; w = w.nextWaiter) { @@ -1979,14 +2214,15 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable /** * Returns a collection containing those threads that may be - * waiting on this Condition. - * Implements {@link AbstractQueuedSynchronizer#getWaitingThreads} + * waiting on this Condition. + * Implements {@link AbstractQueuedSynchronizer#getWaitingThreads}. + * * @return the collection of threads * @throws IllegalMonitorStateException if {@link #isHeldExclusively} - * returns false + * returns {@code false} */ protected final Collection<Thread> getWaitingThreads() { - if (!isHeldExclusively()) + if (!isHeldExclusively()) throw new IllegalMonitorStateException(); ArrayList<Thread> list = new ArrayList<Thread>(); for (Node w = firstWaiter; w != null; w = w.nextWaiter) { @@ -2016,6 +2252,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable private static final long headOffset; private static final long tailOffset; private static final long waitStatusOffset; + private static final long nextOffset; static { try { @@ -2027,19 +2264,21 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable (AbstractQueuedSynchronizer.class.getDeclaredField("tail")); waitStatusOffset = unsafe.objectFieldOffset (Node.class.getDeclaredField("waitStatus")); - - } catch(Exception ex) { throw new Error(ex); } + nextOffset = unsafe.objectFieldOffset + (Node.class.getDeclaredField("next")); + + } catch (Exception ex) { throw new Error(ex); } } /** - * CAS head field. Used only by enq + * CAS head field. Used only by enq. */ private final boolean compareAndSetHead(Node update) { return unsafe.compareAndSwapObject(this, headOffset, null, update); } - + /** - * CAS tail field. Used only by enq + * CAS tail field. Used only by enq. */ private final boolean compareAndSetTail(Node expect, Node update) { return unsafe.compareAndSwapObject(this, tailOffset, expect, update); @@ -2048,11 +2287,19 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable /** * CAS waitStatus field of a node. */ - private final static boolean compareAndSetWaitStatus(Node node, - int expect, + private final static boolean compareAndSetWaitStatus(Node node, + int expect, int update) { - return unsafe.compareAndSwapInt(node, waitStatusOffset, + return unsafe.compareAndSwapInt(node, waitStatusOffset, expect, update); } + /** + * CAS next field of a node. + */ + private final static boolean compareAndSetNext(Node node, + Node expect, + Node update) { + return unsafe.compareAndSwapObject(node, nextOffset, expect, update); + } } diff --git a/concurrent/src/main/java/java/util/concurrent/locks/Condition.java b/concurrent/src/main/java/java/util/concurrent/locks/Condition.java index 7a8ca5b..03be58f 100644 --- a/concurrent/src/main/java/java/util/concurrent/locks/Condition.java +++ b/concurrent/src/main/java/java/util/concurrent/locks/Condition.java @@ -9,13 +9,13 @@ import java.util.concurrent.*; import java.util.Date; /** - * <tt>Condition</tt> factors out the <tt>Object</tt> monitor + * {@code Condition} factors out the {@code Object} monitor * methods ({@link Object#wait() wait}, {@link Object#notify notify} * and {@link Object#notifyAll notifyAll}) into distinct objects to * give the effect of having multiple wait-sets per object, by * combining them with the use of arbitrary {@link Lock} implementations. - * Where a <tt>Lock</tt> replaces the use of <tt>synchronized</tt> methods - * and statements, a <tt>Condition</tt> replaces the use of the Object + * Where a {@code Lock} replaces the use of {@code synchronized} methods + * and statements, a {@code Condition} replaces the use of the Object * monitor methods. * * <p>Conditions (also known as <em>condition queues</em> or @@ -26,37 +26,37 @@ import java.util.Date; * must be protected, so a lock of some form is associated with the * condition. The key property that waiting for a condition provides * is that it <em>atomically</em> releases the associated lock and - * suspends the current thread, just like <tt>Object.wait</tt>. + * suspends the current thread, just like {@code Object.wait}. * - * <p>A <tt>Condition</tt> instance is intrinsically bound to a lock. - * To obtain a <tt>Condition</tt> instance for a particular {@link Lock} + * <p>A {@code Condition} instance is intrinsically bound to a lock. + * To obtain a {@code Condition} instance for a particular {@link Lock} * instance use its {@link Lock#newCondition newCondition()} method. * * <p>As an example, suppose we have a bounded buffer which supports - * <tt>put</tt> and <tt>take</tt> methods. If a - * <tt>take</tt> is attempted on an empty buffer, then the thread will block - * until an item becomes available; if a <tt>put</tt> is attempted on a + * {@code put} and {@code take} methods. If a + * {@code take} is attempted on an empty buffer, then the thread will block + * until an item becomes available; if a {@code put} is attempted on a * full buffer, then the thread will block until a space becomes available. - * We would like to keep waiting <tt>put</tt> threads and <tt>take</tt> + * We would like to keep waiting {@code put} threads and {@code take} * threads in separate wait-sets so that we can use the optimization of * only notifying a single thread at a time when items or spaces become - * available in the buffer. This can be achieved using two + * available in the buffer. This can be achieved using two * {@link Condition} instances. * <pre> * class BoundedBuffer { - * <b>Lock lock = new ReentrantLock();</b> + * <b>final Lock lock = new ReentrantLock();</b> * final Condition notFull = <b>lock.newCondition(); </b> * final Condition notEmpty = <b>lock.newCondition(); </b> * - * Object[] items = new Object[100]; + * final Object[] items = new Object[100]; * int putptr, takeptr, count; * * public void put(Object x) throws InterruptedException { * <b>lock.lock(); * try {</b> - * while (count == items.length) + * while (count == items.length) * <b>notFull.await();</b> - * items[putptr] = x; + * items[putptr] = x; * if (++putptr == items.length) putptr = 0; * ++count; * <b>notEmpty.signal();</b> @@ -68,9 +68,9 @@ import java.util.Date; * public Object take() throws InterruptedException { * <b>lock.lock(); * try {</b> - * while (count == 0) + * while (count == 0) * <b>notEmpty.await();</b> - * Object x = items[takeptr]; + * Object x = items[takeptr]; * if (++takeptr == items.length) takeptr = 0; * --count; * <b>notFull.signal();</b> @@ -78,7 +78,7 @@ import java.util.Date; * <b>} finally { * lock.unlock(); * }</b> - * } + * } * } * </pre> * @@ -86,61 +86,63 @@ import java.util.Date; * this functionality, so there is no reason to implement this * sample usage class.) * - * <p>A <tt>Condition</tt> implementation can provide behavior and semantics - * that is - * different from that of the <tt>Object</tt> monitor methods, such as - * guaranteed ordering for notifications, or not requiring a lock to be held + * <p>A {@code Condition} implementation can provide behavior and semantics + * that is + * different from that of the {@code Object} monitor methods, such as + * guaranteed ordering for notifications, or not requiring a lock to be held * when performing notifications. - * If an implementation provides such specialized semantics then the + * If an implementation provides such specialized semantics then the * implementation must document those semantics. * - * <p>Note that <tt>Condition</tt> instances are just normal objects and can - * themselves be used as the target in a <tt>synchronized</tt> statement, + * <p>Note that {@code Condition} instances are just normal objects and can + * themselves be used as the target in a {@code synchronized} statement, * and can have their own monitor {@link Object#wait wait} and * {@link Object#notify notification} methods invoked. - * Acquiring the monitor lock of a <tt>Condition</tt> instance, or using its + * Acquiring the monitor lock of a {@code Condition} instance, or using its * monitor methods, has no specified relationship with acquiring the - * {@link Lock} associated with that <tt>Condition</tt> or the use of its - * {@link #await waiting} and {@link #signal signalling} methods. - * It is recommended that to avoid confusion you never use <tt>Condition</tt> + * {@link Lock} associated with that {@code Condition} or the use of its + * {@linkplain #await waiting} and {@linkplain #signal signalling} methods. + * It is recommended that to avoid confusion you never use {@code Condition} * instances in this way, except perhaps within their own implementation. * - * <p>Except where noted, passing a <tt>null</tt> value for any parameter + * <p>Except where noted, passing a {@code null} value for any parameter * will result in a {@link NullPointerException} being thrown. * * <h3>Implementation Considerations</h3> * - * <p>When waiting upon a <tt>Condition</tt>, a "<em>spurious - * wakeup</em>" is permitted to occur, in + * <p>When waiting upon a {@code Condition}, a "<em>spurious + * wakeup</em>" is permitted to occur, in * general, as a concession to the underlying platform semantics. * This has little practical impact on most application programs as a - * <tt>Condition</tt> should always be waited upon in a loop, testing + * {@code Condition} should always be waited upon in a loop, testing * the state predicate that is being waited for. An implementation is - * free to remove the possibility of spurious wakeups but it is + * free to remove the possibility of spurious wakeups but it is * recommended that applications programmers always assume that they can * occur and so always wait in a loop. * - * <p>The three forms of condition waiting - * (interruptible, non-interruptible, and timed) may differ in their ease of + * <p>The three forms of condition waiting + * (interruptible, non-interruptible, and timed) may differ in their ease of * implementation on some platforms and in their performance characteristics. - * In particular, it may be difficult to provide these features and maintain - * specific semantics such as ordering guarantees. - * Further, the ability to interrupt the actual suspension of the thread may + * In particular, it may be difficult to provide these features and maintain + * specific semantics such as ordering guarantees. + * Further, the ability to interrupt the actual suspension of the thread may * not always be feasible to implement on all platforms. - * <p>Consequently, an implementation is not required to define exactly the - * same guarantees or semantics for all three forms of waiting, nor is it + * + * <p>Consequently, an implementation is not required to define exactly the + * same guarantees or semantics for all three forms of waiting, nor is it * required to support interruption of the actual suspension of the thread. + * * <p>An implementation is required to - * clearly document the semantics and guarantees provided by each of the - * waiting methods, and when an implementation does support interruption of - * thread suspension then it must obey the interruption semantics as defined + * clearly document the semantics and guarantees provided by each of the + * waiting methods, and when an implementation does support interruption of + * thread suspension then it must obey the interruption semantics as defined * in this interface. - * <p>As interruption generally implies cancellation, and checks for + * + * <p>As interruption generally implies cancellation, and checks for * interruption are often infrequent, an implementation can favor responding * to an interrupt over normal method return. This is true even if it can be - * shown that the interrupt occurred after another action may have unblocked - * the thread. An implementation should document this behavior. - * + * shown that the interrupt occurred after another action that may have + * unblocked the thread. An implementation should document this behavior. * * @since 1.5 * @author Doug Lea @@ -148,21 +150,21 @@ import java.util.Date; public interface Condition { /** - * Causes the current thread to wait until it is signalled or - * {@link Thread#interrupt interrupted}. + * Causes the current thread to wait until it is signalled or + * {@linkplain Thread#interrupt interrupted}. * - * <p>The lock associated with this <tt>Condition</tt> is atomically - * released and the current thread becomes disabled for thread scheduling + * <p>The lock associated with this {@code Condition} is atomically + * released and the current thread becomes disabled for thread scheduling * purposes and lies dormant until <em>one</em> of four things happens: * <ul> - * <li>Some other thread invokes the {@link #signal} method for this - * <tt>Condition</tt> and the current thread happens to be chosen as the + * <li>Some other thread invokes the {@link #signal} method for this + * {@code Condition} and the current thread happens to be chosen as the * thread to be awakened; or - * <li>Some other thread invokes the {@link #signalAll} method for this - * <tt>Condition</tt>; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread, and interruption of thread suspension is supported; or - * <li>A "<em>spurious wakeup</em>" occurs + * <li>Some other thread invokes the {@link #signalAll} method for this + * {@code Condition}; or + * <li>Some other thread {@linkplain Thread#interrupt interrupts} the + * current thread, and interruption of thread suspension is supported; or + * <li>A "<em>spurious wakeup</em>" occurs. * </ul> * * <p>In all cases, before this method can return the current thread must @@ -171,20 +173,21 @@ public interface Condition { * * <p>If the current thread: * <ul> - * <li>has its interrupted status set on entry to this method; or - * <li>is {@link Thread#interrupt interrupted} while waiting - * and interruption of thread suspension is supported, + * <li>has its interrupted status set on entry to this method; or + * <li>is {@linkplain Thread#interrupt interrupted} while waiting + * and interruption of thread suspension is supported, * </ul> - * then {@link InterruptedException} is thrown and the current thread's + * then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. It is not specified, in the first * case, whether or not the test for interruption occurs before the lock * is released. - * + * * <p><b>Implementation Considerations</b> + * * <p>The current thread is assumed to hold the lock associated with this - * <tt>Condition</tt> when this method is called. + * {@code Condition} when this method is called. * It is up to the implementation to determine if this is - * the case and if not, how to respond. Typically, an exception will be + * the case and if not, how to respond. Typically, an exception will be * thrown (such as {@link IllegalMonitorStateException}) and the * implementation must document that fact. * @@ -193,62 +196,62 @@ public interface Condition { * must ensure that the signal is redirected to another waiting thread, if * there is one. * - * @throws InterruptedException if the current thread is interrupted (and - * interruption of thread suspension is supported). - **/ + * @throws InterruptedException if the current thread is interrupted + * (and interruption of thread suspension is supported) + */ void await() throws InterruptedException; /** * Causes the current thread to wait until it is signalled. * - * <p>The lock associated with this condition is atomically - * released and the current thread becomes disabled for thread scheduling + * <p>The lock associated with this condition is atomically + * released and the current thread becomes disabled for thread scheduling * purposes and lies dormant until <em>one</em> of three things happens: * <ul> - * <li>Some other thread invokes the {@link #signal} method for this - * <tt>Condition</tt> and the current thread happens to be chosen as the + * <li>Some other thread invokes the {@link #signal} method for this + * {@code Condition} and the current thread happens to be chosen as the * thread to be awakened; or - * <li>Some other thread invokes the {@link #signalAll} method for this - * <tt>Condition</tt>; or - * <li>A "<em>spurious wakeup</em>" occurs + * <li>Some other thread invokes the {@link #signalAll} method for this + * {@code Condition}; or + * <li>A "<em>spurious wakeup</em>" occurs. * </ul> * * <p>In all cases, before this method can return the current thread must * re-acquire the lock associated with this condition. When the * thread returns it is <em>guaranteed</em> to hold this lock. * - * <p>If the current thread's interrupt status is set when it enters - * this method, or it is {@link Thread#interrupt interrupted} + * <p>If the current thread's interrupted status is set when it enters + * this method, or it is {@linkplain Thread#interrupt interrupted} * while waiting, it will continue to wait until signalled. When it finally - * returns from this method its <em>interrupted status</em> will still + * returns from this method its interrupted status will still * be set. - * + * * <p><b>Implementation Considerations</b> + * * <p>The current thread is assumed to hold the lock associated with this - * <tt>Condition</tt> when this method is called. + * {@code Condition} when this method is called. * It is up to the implementation to determine if this is - * the case and if not, how to respond. Typically, an exception will be + * the case and if not, how to respond. Typically, an exception will be * thrown (such as {@link IllegalMonitorStateException}) and the * implementation must document that fact. - * - **/ + */ void awaitUninterruptibly(); /** * Causes the current thread to wait until it is signalled or interrupted, * or the specified waiting time elapses. * - * <p>The lock associated with this condition is atomically - * released and the current thread becomes disabled for thread scheduling + * <p>The lock associated with this condition is atomically + * released and the current thread becomes disabled for thread scheduling * purposes and lies dormant until <em>one</em> of five things happens: * <ul> - * <li>Some other thread invokes the {@link #signal} method for this - * <tt>Condition</tt> and the current thread happens to be chosen as the - * thread to be awakened; or - * <li>Some other thread invokes the {@link #signalAll} method for this - * <tt>Condition</tt>; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread, and interruption of thread suspension is supported; or + * <li>Some other thread invokes the {@link #signal} method for this + * {@code Condition} and the current thread happens to be chosen as the + * thread to be awakened; or + * <li>Some other thread invokes the {@link #signalAll} method for this + * {@code Condition}; or + * <li>Some other thread {@linkplain Thread#interrupt interrupts} the + * current thread, and interruption of thread suspension is supported; or * <li>The specified waiting time elapses; or * <li>A "<em>spurious wakeup</em>" occurs. * </ul> @@ -259,17 +262,17 @@ public interface Condition { * * <p>If the current thread: * <ul> - * <li>has its interrupted status set on entry to this method; or - * <li>is {@link Thread#interrupt interrupted} while waiting - * and interruption of thread suspension is supported, + * <li>has its interrupted status set on entry to this method; or + * <li>is {@linkplain Thread#interrupt interrupted} while waiting + * and interruption of thread suspension is supported, * </ul> - * then {@link InterruptedException} is thrown and the current thread's + * then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. It is not specified, in the first * case, whether or not the test for interruption occurs before the lock * is released. * * <p>The method returns an estimate of the number of nanoseconds - * remaining to wait given the supplied <tt>nanosTimeout</tt> + * remaining to wait given the supplied {@code nanosTimeout} * value upon return, or a value less than or equal to zero if it * timed out. This value can be used to determine whether and how * long to re-wait in cases where the wait returns but an awaited @@ -285,7 +288,7 @@ public interface Condition { * else * return false; * } - * // ... + * // ... * } * </pre> * @@ -296,10 +299,11 @@ public interface Condition { * than specified when re-waits occur. * * <p><b>Implementation Considerations</b> + * * <p>The current thread is assumed to hold the lock associated with this - * <tt>Condition</tt> when this method is called. + * {@code Condition} when this method is called. * It is up to the implementation to determine if this is - * the case and if not, how to respond. Typically, an exception will be + * the case and if not, how to respond. Typically, an exception will be * thrown (such as {@link IllegalMonitorStateException}) and the * implementation must document that fact. * @@ -310,13 +314,14 @@ public interface Condition { * there is one. * * @param nanosTimeout the maximum time to wait, in nanoseconds - * @return A value less than or equal to zero if the wait has - * timed out; otherwise an estimate, that - * is strictly less than the <tt>nanosTimeout</tt> argument, - * of the time still remaining when this method returned. - * - * @throws InterruptedException if the current thread is interrupted (and - * interruption of thread suspension is supported). + * @return an estimate of the {@code nanosTimeout} value minus + * the time spent waiting upon return from this method. + * A positive value may be used as the argument to a + * subsequent call to this method to finish waiting out + * the desired time. A value less than or equal to zero + * indicates that no time remains. + * @throws InterruptedException if the current thread is interrupted + * (and interruption of thread suspension is supported) */ long awaitNanos(long nanosTimeout) throws InterruptedException; @@ -328,29 +333,29 @@ public interface Condition { * awaitNanos(unit.toNanos(time)) > 0 * </pre> * @param time the maximum time to wait - * @param unit the time unit of the <tt>time</tt> argument. - * @return <tt>false</tt> if the waiting time detectably elapsed - * before return from the method, else <tt>true</tt>. - * @throws InterruptedException if the current thread is interrupted (and - * interruption of thread suspension is supported). + * @param unit the time unit of the {@code time} argument + * @return {@code false} if the waiting time detectably elapsed + * before return from the method, else {@code true} + * @throws InterruptedException if the current thread is interrupted + * (and interruption of thread suspension is supported) */ boolean await(long time, TimeUnit unit) throws InterruptedException; - + /** * Causes the current thread to wait until it is signalled or interrupted, * or the specified deadline elapses. * - * <p>The lock associated with this condition is atomically - * released and the current thread becomes disabled for thread scheduling + * <p>The lock associated with this condition is atomically + * released and the current thread becomes disabled for thread scheduling * purposes and lies dormant until <em>one</em> of five things happens: * <ul> - * <li>Some other thread invokes the {@link #signal} method for this - * <tt>Condition</tt> and the current thread happens to be chosen as the - * thread to be awakened; or - * <li>Some other thread invokes the {@link #signalAll} method for this - * <tt>Condition</tt>; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread, and interruption of thread suspension is supported; or + * <li>Some other thread invokes the {@link #signal} method for this + * {@code Condition} and the current thread happens to be chosen as the + * thread to be awakened; or + * <li>Some other thread invokes the {@link #signalAll} method for this + * {@code Condition}; or + * <li>Some other thread {@linkplain Thread#interrupt interrupts} the + * current thread, and interruption of thread suspension is supported; or * <li>The specified deadline elapses; or * <li>A "<em>spurious wakeup</em>" occurs. * </ul> @@ -362,11 +367,11 @@ public interface Condition { * * <p>If the current thread: * <ul> - * <li>has its interrupted status set on entry to this method; or - * <li>is {@link Thread#interrupt interrupted} while waiting - * and interruption of thread suspension is supported, + * <li>has its interrupted status set on entry to this method; or + * <li>is {@linkplain Thread#interrupt interrupted} while waiting + * and interruption of thread suspension is supported, * </ul> - * then {@link InterruptedException} is thrown and the current thread's + * then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. It is not specified, in the first * case, whether or not the test for interruption occurs before the lock * is released. @@ -378,20 +383,21 @@ public interface Condition { * synchronized boolean aMethod(Date deadline) { * boolean stillWaiting = true; * while (!conditionBeingWaitedFor) { - * if (stillwaiting) + * if (stillWaiting) * stillWaiting = theCondition.awaitUntil(deadline); * else * return false; * } - * // ... + * // ... * } * </pre> * * <p><b>Implementation Considerations</b> + * * <p>The current thread is assumed to hold the lock associated with this - * <tt>Condition</tt> when this method is called. + * {@code Condition} when this method is called. * It is up to the implementation to determine if this is - * the case and if not, how to respond. Typically, an exception will be + * the case and if not, how to respond. Typically, an exception will be * thrown (such as {@link IllegalMonitorStateException}) and the * implementation must document that fact. * @@ -401,13 +407,11 @@ public interface Condition { * must ensure that the signal is redirected to another waiting thread, if * there is one. * - * * @param deadline the absolute time to wait until - * @return <tt>false</tt> if the deadline has - * elapsed upon return, else <tt>true</tt>. - * - * @throws InterruptedException if the current thread is interrupted (and - * interruption of thread suspension is supported). + * @return {@code false} if the deadline has elapsed upon return, else + * {@code true} + * @throws InterruptedException if the current thread is interrupted + * (and interruption of thread suspension is supported) */ boolean awaitUntil(Date deadline) throws InterruptedException; @@ -416,8 +420,8 @@ public interface Condition { * * <p>If any threads are waiting on this condition then one * is selected for waking up. That thread must then re-acquire the - * lock before returning from <tt>await</tt>. - **/ + * lock before returning from {@code await}. + */ void signal(); /** @@ -425,15 +429,7 @@ public interface Condition { * * <p>If any threads are waiting on this condition then they are * all woken up. Each thread must re-acquire the lock before it can - * return from <tt>await</tt>. - **/ + * return from {@code await}. + */ void signalAll(); - } - - - - - - - diff --git a/concurrent/src/main/java/java/util/concurrent/locks/Lock.java b/concurrent/src/main/java/java/util/concurrent/locks/Lock.java index f07b72e..4b9abd6 100644 --- a/concurrent/src/main/java/java/util/concurrent/locks/Lock.java +++ b/concurrent/src/main/java/java/util/concurrent/locks/Lock.java @@ -8,8 +8,8 @@ package java.util.concurrent.locks; import java.util.concurrent.TimeUnit; /** - * <tt>Lock</tt> implementations provide more extensive locking - * operations than can be obtained using <tt>synchronized</tt> methods + * {@code Lock} implementations provide more extensive locking + * operations than can be obtained using {@code synchronized} methods * and statements. They allow more flexible structuring, may have * quite different properties, and may support multiple associated * {@link Condition} objects. @@ -19,17 +19,16 @@ import java.util.concurrent.TimeUnit; * shared resource: only one thread at a time can acquire the lock and * all access to the shared resource requires that the lock be * acquired first. However, some locks may allow concurrent access to - * a shared resource, such as the read lock of a {@link - * ReadWriteLock}. + * a shared resource, such as the read lock of a {@link ReadWriteLock}. * - * <p>The use of <tt>synchronized</tt> methods or statements provides + * <p>The use of {@code synchronized} methods or statements provides * access to the implicit monitor lock associated with every object, but * forces all lock acquisition and release to occur in a block-structured way: * when multiple locks are acquired they must be released in the opposite * order, and all locks must be released in the same lexical scope in which * they were acquired. * - * <p>While the scoping mechanism for <tt>synchronized</tt> methods + * <p>While the scoping mechanism for {@code synchronized} methods * and statements makes it much easier to program with monitor locks, * and helps avoid many common programming errors involving locks, * there are occasions where you need to work with locks in a more @@ -38,18 +37,18 @@ import java.util.concurrent.TimeUnit; * "hand-over-hand" or "chain locking": you * acquire the lock of node A, then node B, then release A and acquire * C, then release B and acquire D and so on. Implementations of the - * <tt>Lock</tt> interface enable the use of such techniques by + * {@code Lock} interface enable the use of such techniques by * allowing a lock to be acquired and released in different scopes, * and allowing multiple locks to be acquired and released in any * order. * * <p>With this increased flexibility comes additional * responsibility. The absence of block-structured locking removes the - * automatic release of locks that occurs with <tt>synchronized</tt> + * automatic release of locks that occurs with {@code synchronized} * methods and statements. In most cases, the following idiom * should be used: * - * <pre><tt> Lock l = ...; + * <pre><tt> Lock l = ...; * l.lock(); * try { * // access the resource protected by this lock @@ -63,39 +62,42 @@ import java.util.concurrent.TimeUnit; * held is protected by try-finally or try-catch to ensure that the * lock is released when necessary. * - * <p><tt>Lock</tt> implementations provide additional functionality - * over the use of <tt>synchronized</tt> methods and statements by + * <p>{@code Lock} implementations provide additional functionality + * over the use of {@code synchronized} methods and statements by * providing a non-blocking attempt to acquire a lock ({@link * #tryLock()}), an attempt to acquire the lock that can be * interrupted ({@link #lockInterruptibly}, and an attempt to acquire * the lock that can timeout ({@link #tryLock(long, TimeUnit)}). * - * <p>A <tt>Lock</tt> class can also provide behavior and semantics + * <p>A {@code Lock} class can also provide behavior and semantics * that is quite different from that of the implicit monitor lock, * such as guaranteed ordering, non-reentrant usage, or deadlock * detection. If an implementation provides such specialized semantics * then the implementation must document those semantics. * - * <p>Note that <tt>Lock</tt> instances are just normal objects and can - * themselves be used as the target in a <tt>synchronized</tt> statement. + * <p>Note that {@code Lock} instances are just normal objects and can + * themselves be used as the target in a {@code synchronized} statement. * Acquiring the - * monitor lock of a <tt>Lock</tt> instance has no specified relationship - * with invoking any of the {@link #lock} methods of that instance. - * It is recommended that to avoid confusion you never use <tt>Lock</tt> + * monitor lock of a {@code Lock} instance has no specified relationship + * with invoking any of the {@link #lock} methods of that instance. + * It is recommended that to avoid confusion you never use {@code Lock} * instances in this way, except within their own implementation. * - * <p>Except where noted, passing a <tt>null</tt> value for any + * <p>Except where noted, passing a {@code null} value for any * parameter will result in a {@link NullPointerException} being * thrown. * * <h3>Memory Synchronization</h3> - * <p>All <tt>Lock</tt> implementations <em>must</em> enforce the same - * memory synchronization semantics as provided by the built-in monitor lock: + * + * <p>All {@code Lock} implementations <em>must</em> enforce the same + * memory synchronization semantics as provided by the built-in monitor + * lock, as described in <a href="http://java.sun.com/docs/books/jls/"> + * The Java Language Specification, Third Edition (17.4 Memory Model)</a>: * <ul> - * <li>A successful lock operation acts like a successful - * <tt>monitorEnter</tt> action - * <li>A successful <tt>unlock</tt> operation acts like a successful - * <tt>monitorExit</tt> action + * <li>A successful {@code lock} operation has the same memory + * synchronization effects as a successful <em>Lock</em> action. + * <li>A successful {@code unlock} operation has the same + * memory synchronization effects as a successful <em>Unlock</em> action. * </ul> * * Unsuccessful locking and unlocking operations, and reentrant @@ -108,7 +110,7 @@ import java.util.concurrent.TimeUnit; * non-interruptible, and timed) may differ in their performance * characteristics, ordering guarantees, or other implementation * qualities. Further, the ability to interrupt the <em>ongoing</em> - * acquisition of a lock may not be available in a given <tt>Lock</tt> + * acquisition of a lock may not be available in a given {@code Lock} * class. Consequently, an implementation is not required to define * exactly the same guarantees or semantics for all three forms of * lock acquisition, nor is it required to support interruption of an @@ -119,12 +121,11 @@ import java.util.concurrent.TimeUnit; * acquisition is supported: which is either totally, or only on * method entry. * - * <p>As interruption generally implies cancellation, and checks for + * <p>As interruption generally implies cancellation, and checks for * interruption are often infrequent, an implementation can favor responding * to an interrupt over normal method return. This is true even if it can be * shown that the interrupt occurred after another action may have unblocked - * the thread. An implementation should document this behavior. - * + * the thread. An implementation should document this behavior. * * @see ReentrantLock * @see Condition @@ -132,45 +133,50 @@ import java.util.concurrent.TimeUnit; * * @since 1.5 * @author Doug Lea - * - **/ + */ public interface Lock { /** * Acquires the lock. - * <p>If the lock is not available then - * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until the lock has been acquired. + * + * <p>If the lock is not available then the current thread becomes + * disabled for thread scheduling purposes and lies dormant until the + * lock has been acquired. + * * <p><b>Implementation Considerations</b> - * <p>A <tt>Lock</tt> implementation may be able to detect - * erroneous use of the lock, such as an invocation that would cause - * deadlock, and may throw an (unchecked) exception in such circumstances. - * The circumstances and the exception type must be documented by that - * <tt>Lock</tt> implementation. * - **/ + * <p>A {@code Lock} implementation may be able to detect erroneous use + * of the lock, such as an invocation that would cause deadlock, and + * may throw an (unchecked) exception in such circumstances. The + * circumstances and the exception type must be documented by that + * {@code Lock} implementation. + */ void lock(); /** - * Acquires the lock unless the current thread is - * {@link Thread#interrupt interrupted}. + * Acquires the lock unless the current thread is + * {@linkplain Thread#interrupt interrupted}. + * * <p>Acquires the lock if it is available and returns immediately. - * <p>If the lock is not available then - * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until one of two things happens: + * + * <p>If the lock is not available then the current thread becomes + * disabled for thread scheduling purposes and lies dormant until + * one of two things happens: + * * <ul> * <li>The lock is acquired by the current thread; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread, and interruption of lock acquisition is supported. + * <li>Some other thread {@linkplain Thread#interrupt interrupts} the + * current thread, and interruption of lock acquisition is supported. * </ul> + * * <p>If the current thread: * <ul> - * <li>has its interrupted status set on entry to this method; or - * <li>is {@link Thread#interrupt interrupted} while acquiring - * the lock, and interruption of lock acquisition is supported, + * <li>has its interrupted status set on entry to this method; or + * <li>is {@linkplain Thread#interrupt interrupted} while acquiring the + * lock, and interruption of lock acquisition is supported, * </ul> - * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. * * <p><b>Implementation Considerations</b> * @@ -183,28 +189,26 @@ public interface Lock { * <p>An implementation can favor responding to an interrupt over * normal method return. * - * <p>A <tt>Lock</tt> implementation may be able to detect + * <p>A {@code Lock} implementation may be able to detect * erroneous use of the lock, such as an invocation that would * cause deadlock, and may throw an (unchecked) exception in such * circumstances. The circumstances and the exception type must - * be documented by that <tt>Lock</tt> implementation. - * - * @throws InterruptedException if the current thread is interrupted - * while acquiring the lock (and interruption of lock acquisition is - * supported). - * - * @see Thread#interrupt + * be documented by that {@code Lock} implementation. * - **/ + * @throws InterruptedException if the current thread is + * interrupted while acquiring the lock (and interruption + * of lock acquisition is supported). + */ void lockInterruptibly() throws InterruptedException; - /** * Acquires the lock only if it is free at the time of invocation. + * * <p>Acquires the lock if it is available and returns immediately - * with the value <tt>true</tt>. - * If the lock is not available then this method will return - * immediately with the value <tt>false</tt>. + * with the value {@code true}. + * If the lock is not available then this method will return + * immediately with the value {@code false}. + * * <p>A typical usage idiom for this method would be: * <pre> * Lock lock = ...; @@ -221,109 +225,103 @@ public interface Lock { * This usage ensures that the lock is unlocked if it was acquired, and * doesn't try to unlock if the lock was not acquired. * - * @return <tt>true</tt> if the lock was acquired and <tt>false</tt> - * otherwise. - **/ + * @return {@code true} if the lock was acquired and + * {@code false} otherwise + */ boolean tryLock(); /** * Acquires the lock if it is free within the given waiting time and the - * current thread has not been {@link Thread#interrupt interrupted}. + * current thread has not been {@linkplain Thread#interrupt interrupted}. * * <p>If the lock is available this method returns immediately - * with the value <tt>true</tt>. + * with the value {@code true}. * If the lock is not available then - * the current thread becomes disabled for thread scheduling + * the current thread becomes disabled for thread scheduling * purposes and lies dormant until one of three things happens: * <ul> * <li>The lock is acquired by the current thread; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread, and interruption of lock acquisition is supported; or + * <li>Some other thread {@linkplain Thread#interrupt interrupts} the + * current thread, and interruption of lock acquisition is supported; or * <li>The specified waiting time elapses * </ul> - * <p>If the lock is acquired then the value <tt>true</tt> is returned. + * + * <p>If the lock is acquired then the value {@code true} is returned. + * * <p>If the current thread: * <ul> - * <li>has its interrupted status set on entry to this method; or - * <li>is {@link Thread#interrupt interrupted} while acquiring - * the lock, and interruption of lock acquisition is supported, + * <li>has its interrupted status set on entry to this method; or + * <li>is {@linkplain Thread#interrupt interrupted} while acquiring + * the lock, and interruption of lock acquisition is supported, * </ul> - * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. - * <p>If the specified waiting time elapses then the value <tt>false</tt> + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. + * + * <p>If the specified waiting time elapses then the value {@code false} * is returned. - * If the time is + * If the time is * less than or equal to zero, the method will not wait at all. * * <p><b>Implementation Considerations</b> + * * <p>The ability to interrupt a lock acquisition in some implementations - * may not be possible, and if possible may - * be an expensive operation. + * may not be possible, and if possible may + * be an expensive operation. * The programmer should be aware that this may be the case. An * implementation should document when this is the case. - * <p>An implementation can favor responding to an interrupt over normal + * + * <p>An implementation can favor responding to an interrupt over normal * method return, or reporting a timeout. - * <p>A <tt>Lock</tt> implementation may be able to detect - * erroneous use of the lock, such as an invocation that would cause - * deadlock, and may throw an (unchecked) exception in such circumstances. - * The circumstances and the exception type must be documented by that - * <tt>Lock</tt> implementation. + * + * <p>A {@code Lock} implementation may be able to detect + * erroneous use of the lock, such as an invocation that would cause + * deadlock, and may throw an (unchecked) exception in such circumstances. + * The circumstances and the exception type must be documented by that + * {@code Lock} implementation. * * @param time the maximum time to wait for the lock - * @param unit the time unit of the <tt>time</tt> argument. - * @return <tt>true</tt> if the lock was acquired and <tt>false</tt> - * if the waiting time elapsed before the lock was acquired. + * @param unit the time unit of the {@code time} argument + * @return {@code true} if the lock was acquired and {@code false} + * if the waiting time elapsed before the lock was acquired * * @throws InterruptedException if the current thread is interrupted - * while acquiring the lock (and interruption of lock acquisition is - * supported). - * - * @see Thread#interrupt - * - **/ + * while acquiring the lock (and interruption of lock + * acquisition is supported) + */ boolean tryLock(long time, TimeUnit unit) throws InterruptedException; /** * Releases the lock. + * * <p><b>Implementation Considerations</b> - * <p>A <tt>Lock</tt> implementation will usually impose + * + * <p>A {@code Lock} implementation will usually impose * restrictions on which thread can release a lock (typically only the * holder of the lock can release it) and may throw * an (unchecked) exception if the restriction is violated. * Any restrictions and the exception - * type must be documented by that <tt>Lock</tt> implementation. - **/ + * type must be documented by that {@code Lock} implementation. + */ void unlock(); /** - * Returns a new {@link Condition} instance that is bound to this - * <tt>Lock</tt> instance. - * <p>Before waiting on the condition the lock must be held by the - * current thread. - * A call to {@link Condition#await()} will atomically release the lock + * Returns a new {@link Condition} instance that is bound to this + * {@code Lock} instance. + * + * <p>Before waiting on the condition the lock must be held by the + * current thread. + * A call to {@link Condition#await()} will atomically release the lock * before waiting and re-acquire the lock before the wait returns. + * * <p><b>Implementation Considerations</b> - * <p>The exact operation of the {@link Condition} instance depends on the - * <tt>Lock</tt> implementation and must be documented by that + * + * <p>The exact operation of the {@link Condition} instance depends on + * the {@code Lock} implementation and must be documented by that * implementation. - * - * @return A new {@link Condition} instance for this <tt>Lock</tt> - * instance. - * @throws UnsupportedOperationException if this <tt>Lock</tt> - * implementation does not support conditions. - **/ + * + * @return A new {@link Condition} instance for this {@code Lock} instance + * @throws UnsupportedOperationException if this {@code Lock} + * implementation does not support conditions + */ Condition newCondition(); - } - - - - - - - - - - - - diff --git a/concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java b/concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java index 8603785..b55b874 100644 --- a/concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java +++ b/concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java @@ -13,48 +13,56 @@ import sun.misc.Unsafe; * Basic thread blocking primitives for creating locks and other * synchronization classes. * - * <p>This class associates with each thread that uses it, a permit + * <p>This class associates, with each thread that uses it, a permit * (in the sense of the {@link java.util.concurrent.Semaphore - * Semaphore} class). A call to <tt>park</tt> will return immediately + * Semaphore} class). A call to {@code park} will return immediately * if the permit is available, consuming it in the process; otherwise - * it <em>may</em> block. A call to <tt>unpark</tt> makes the permit + * it <em>may</em> block. A call to {@code unpark} makes the permit * available, if it was not already available. (Unlike with Semaphores * though, permits do not accumulate. There is at most one.) * - * <p>Methods <tt>park</tt> and <tt>unpark</tt> provide efficient + * <p>Methods {@code park} and {@code unpark} provide efficient * means of blocking and unblocking threads that do not encounter the - * problems that cause the deprecated methods <tt>Thread.suspend</tt> - * and <tt>Thread.resume</tt> to be unusable for such purposes: Races - * between one thread invoking <tt>park</tt> and another thread trying - * to <tt>unpark</tt> it will preserve liveness, due to the - * permit. Additionally, <tt>park</tt> will return if the caller's + * problems that cause the deprecated methods {@code Thread.suspend} + * and {@code Thread.resume} to be unusable for such purposes: Races + * between one thread invoking {@code park} and another thread trying + * to {@code unpark} it will preserve liveness, due to the + * permit. Additionally, {@code park} will return if the caller's * thread was interrupted, and timeout versions are supported. The - * <tt>park</tt> method may also return at any other time, for "no + * {@code park} method may also return at any other time, for "no * reason", so in general must be invoked within a loop that rechecks - * conditions upon return. In this sense <tt>park</tt> serves as an + * conditions upon return. In this sense {@code park} serves as an * optimization of a "busy wait" that does not waste as much time - * spinning, but must be paired with an <tt>unpark</tt> to be + * spinning, but must be paired with an {@code unpark} to be * effective. * * <p>These methods are designed to be used as tools for creating * higher-level synchronization utilities, and are not in themselves - * useful for most concurrency control applications. + * useful for most concurrency control applications. The {@code park} + * method is designed for use only in constructions of the form: + * <pre>while (!canProceed()) { ... LockSupport.park(this); }</pre> + * where neither {@code canProceed} nor any other actions prior to the + * call to {@code park} entail locking or blocking. Because only one + * permit is associated with each thread, any intermediary uses of + * {@code park} could interfere with its intended effects. * - * <p><b>Sample Usage.</b> Here is a sketch of a First-in-first-out - * non-reentrant lock class. - * <pre> - * private AtomicBoolean locked = new AtomicBoolean(false); - * private Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>(); + * <p><b>Sample Usage.</b> Here is a sketch of a first-in-first-out + * non-reentrant lock class: + * <pre>{@code + * class FIFOMutex { + * private final AtomicBoolean locked = new AtomicBoolean(false); + * private final Queue<Thread> waiters + * = new ConcurrentLinkedQueue<Thread>(); * - * public void lock() { + * public void lock() { * boolean wasInterrupted = false; * Thread current = Thread.currentThread(); * waiters.add(current); * * // Block while not first in queue or cannot acquire lock - * while (waiters.peek() != current || - * !locked.compareAndSet(false, true)) { - * LockSupport.park(); + * while (waiters.peek() != current || + * !locked.compareAndSet(false, true)) { + * LockSupport.park(this); * if (Thread.interrupted()) // ignore interrupts while waiting * wasInterrupted = true; * } @@ -67,9 +75,8 @@ import sun.misc.Unsafe; * public void unlock() { * locked.set(false); * LockSupport.unpark(waiters.peek()); - * } - * } - * </pre> + * } + * }}</pre> */ @SuppressWarnings("all") public class LockSupport { @@ -80,14 +87,15 @@ public class LockSupport { // END android-changed /** - * Make available the permit for the given thread, if it + * Makes available the permit for the given thread, if it * was not already available. If the thread was blocked on - * <tt>park</tt> then it will unblock. Otherwise, its next call - * to <tt>park</tt> is guaranteed not to block. This operation + * {@code park} then it will unblock. Otherwise, its next call + * to {@code park} is guaranteed not to block. This operation * is not guaranteed to have any effect at all if the given * thread has not been started. - * @param thread the thread to unpark, or <tt>null</tt>, in which case - * this operation has no effect. + * + * @param thread the thread to unpark, or {@code null}, in which case + * this operation has no effect */ public static void unpark(Thread thread) { if (thread != null) @@ -97,20 +105,26 @@ public class LockSupport { /** * Disables the current thread for thread scheduling purposes unless the * permit is available. - * <p>If the permit is available then it is consumed and the call returns - * immediately; otherwise - * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until one of three things happens: + * + * <p>If the permit is available then it is consumed and the call + * returns immediately; otherwise the current thread becomes disabled + * for thread scheduling purposes and lies dormant until one of three + * things happens: + * * <ul> - * <li>Some other thread invokes <tt>unpark</tt> with the current thread - * as the target; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + * + * <li>Some other thread invokes {@link #unpark unpark} with the + * current thread as the target; or + * + * <li>Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or + * * <li>The call spuriously (that is, for no reason) returns. * </ul> - * <p>This method does <em>not</em> report which of these caused the - * method to return. Callers should re-check the conditions which caused - * the thread to park in the first place. Callers may also determine, + * + * <p>This method does <em>not</em> report which of these caused the + * method to return. Callers should re-check the conditions which caused + * the thread to park in the first place. Callers may also determine, * for example, the interrupt status of the thread upon return. */ public static void park() { @@ -120,21 +134,27 @@ public class LockSupport { /** * Disables the current thread for thread scheduling purposes, for up to * the specified waiting time, unless the permit is available. - * <p>If the permit is available then it is consumed and the call returns - * immediately; otherwise - * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until one of four things happens: + * + * <p>If the permit is available then it is consumed and the call + * returns immediately; otherwise the current thread becomes disabled + * for thread scheduling purposes and lies dormant until one of four + * things happens: + * * <ul> - * <li>Some other thread invokes <tt>unpark</tt> with the current thread - * as the target; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + * <li>Some other thread invokes {@link #unpark unpark} with the + * current thread as the target; or + * + * <li>Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or + * * <li>The specified waiting time elapses; or + * * <li>The call spuriously (that is, for no reason) returns. * </ul> - * <p>This method does <em>not</em> report which of these caused the - * method to return. Callers should re-check the conditions which caused - * the thread to park in the first place. Callers may also determine, + * + * <p>This method does <em>not</em> report which of these caused the + * method to return. Callers should re-check the conditions which caused + * the thread to park in the first place. Callers may also determine, * for example, the interrupt status of the thread, or the elapsed time * upon return. * @@ -142,37 +162,40 @@ public class LockSupport { */ public static void parkNanos(long nanos) { if (nanos > 0) - unsafe.park(false, nanos); + unsafe.park(false, nanos); } /** * Disables the current thread for thread scheduling purposes, until * the specified deadline, unless the permit is available. - * <p>If the permit is available then it is consumed and the call returns - * immediately; otherwise - * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until one of four things happens: + * + * <p>If the permit is available then it is consumed and the call + * returns immediately; otherwise the current thread becomes disabled + * for thread scheduling purposes and lies dormant until one of four + * things happens: + * * <ul> - * <li>Some other thread invokes <tt>unpark</tt> with the current thread - * as the target; or - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + * <li>Some other thread invokes {@link #unpark unpark} with the + * current thread as the target; or + * + * <li>Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or + * * <li>The specified deadline passes; or + * * <li>The call spuriously (that is, for no reason) returns. * </ul> - * <p>This method does <em>not</em> report which of these caused the - * method to return. Callers should re-check the conditions which caused - * the thread to park in the first place. Callers may also determine, + * + * <p>This method does <em>not</em> report which of these caused the + * method to return. Callers should re-check the conditions which caused + * the thread to park in the first place. Callers may also determine, * for example, the interrupt status of the thread, or the current time * upon return. * - * @param deadline the absolute time, in milliseconds from the Epoch, to - * wait until + * @param deadline the absolute time, in milliseconds from the Epoch, + * to wait until */ public static void parkUntil(long deadline) { - unsafe.park(true, deadline); + unsafe.park(true, deadline); } - } - - diff --git a/concurrent/src/main/java/java/util/concurrent/locks/ReadWriteLock.java b/concurrent/src/main/java/java/util/concurrent/locks/ReadWriteLock.java index c7116d5..484f68d 100644 --- a/concurrent/src/main/java/java/util/concurrent/locks/ReadWriteLock.java +++ b/concurrent/src/main/java/java/util/concurrent/locks/ReadWriteLock.java @@ -12,11 +12,18 @@ package java.util.concurrent.locks; * The {@link #readLock read lock} may be held simultaneously by * multiple reader threads, so long as there are no writers. The * {@link #writeLock write lock} is exclusive. - * + * + * <p>All <tt>ReadWriteLock</tt> implementations must guarantee that + * the memory synchronization effects of <tt>writeLock</tt> operations + * (as specified in the {@link Lock} interface) also hold with respect + * to the associated <tt>readLock</tt>. That is, a thread successfully + * acquiring the read lock will see all updates made upon previous + * release of the write lock. + * * <p>A read-write lock allows for a greater level of concurrency in - * accessing shared data, than that permitted by a mutual exclusion lock. + * accessing shared data than that permitted by a mutual exclusion lock. * It exploits the fact that while only a single thread at a time (a - * <em>writer</em> thread) can modify the shared data, in many cases any + * <em>writer</em> thread) can modify the shared data, in many cases any * number of threads can concurrently read the data (hence <em>reader</em> * threads). * In theory, the increase in concurrency permitted by the use of a read-write @@ -27,7 +34,7 @@ package java.util.concurrent.locks; * * <p>Whether or not a read-write lock will improve performance over the use * of a mutual exclusion lock depends on the frequency that the data is - * read compared to being modified, the duration of the read and write + * read compared to being modified, the duration of the read and write * operations, and the contention for the data - that is, the number of * threads that will try to read or write the data at the same time. * For example, a collection that is initially populated with data and @@ -56,14 +63,14 @@ package java.util.concurrent.locks; * lengthy delays for a write if the readers are frequent and long-lived as * expected. Fair, or "in-order" implementations are also possible. * - * <li>Determining whether readers that request the read lock while a + * <li>Determining whether readers that request the read lock while a * reader is active and a writer is waiting, are granted the read lock. * Preference to the reader can delay the writer indefinitely, while - * preference to the write can reduce the potential for concurrency. + * preference to the writer can reduce the potential for concurrency. * * <li>Determining whether the locks are reentrant: can a thread with the - * write lock reacquire it? can it acquire a read lock while holding the - * write lock? is the read lock itself reentrant? + * write lock reacquire it? Can it acquire a read lock while holding the + * write lock? Is the read lock itself reentrant? * * <li>Can the write lock be downgraded to a read lock without allowing * an intervening writer? Can a read lock be upgraded to a write lock, diff --git a/concurrent/src/main/java/java/util/concurrent/locks/ReentrantLock.java b/concurrent/src/main/java/java/util/concurrent/locks/ReentrantLock.java index bf3d653..bf2ac38 100644 --- a/concurrent/src/main/java/java/util/concurrent/locks/ReentrantLock.java +++ b/concurrent/src/main/java/java/util/concurrent/locks/ReentrantLock.java @@ -12,19 +12,19 @@ import java.util.concurrent.atomic.*; /** * A reentrant mutual exclusion {@link Lock} with the same basic * behavior and semantics as the implicit monitor lock accessed using - * <tt>synchronized</tt> methods and statements, but with extended + * {@code synchronized} methods and statements, but with extended * capabilities. * - * <p> A <tt>ReentrantLock</tt> is <em>owned</em> by the thread last + * <p>A {@code ReentrantLock} is <em>owned</em> by the thread last * successfully locking, but not yet unlocking it. A thread invoking - * <tt>lock</tt> will return, successfully acquiring the lock, when + * {@code lock} will return, successfully acquiring the lock, when * the lock is not owned by another thread. The method will return * immediately if the current thread already owns the lock. This can * be checked using methods {@link #isHeldByCurrentThread}, and {@link - * #getHoldCount}. + * #getHoldCount}. * - * <p> The constructor for this class accepts an optional - * <em>fairness</em> parameter. When set <tt>true</tt>, under + * <p>The constructor for this class accepts an optional + * <em>fairness</em> parameter. When set {@code true}, under * contention, locks favor granting access to the longest-waiting * thread. Otherwise this lock does not guarantee any particular * access order. Programs using fair locks accessed by many threads @@ -36,9 +36,12 @@ import java.util.concurrent.atomic.*; * fair lock may obtain it multiple times in succession while other * active threads are not progressing and not currently holding the * lock. + * Also note that the untimed {@link #tryLock() tryLock} method does not + * honor the fairness setting. It will succeed if the lock + * is available even if other threads are waiting. * - * <p> It is recommended practice to <em>always</em> immediately - * follow a call to <tt>lock</tt> with a <tt>try</tt> block, most + * <p>It is recommended practice to <em>always</em> immediately + * follow a call to {@code lock} with a {@code try} block, most * typically in a before/after construction such as: * * <pre> @@ -46,7 +49,7 @@ import java.util.concurrent.atomic.*; * private final ReentrantLock lock = new ReentrantLock(); * // ... * - * public void m() { + * public void m() { * lock.lock(); // block until condition holds * try { * // ... method body @@ -58,21 +61,21 @@ import java.util.concurrent.atomic.*; * </pre> * * <p>In addition to implementing the {@link Lock} interface, this - * class defines methods <tt>isLocked</tt> and - * <tt>getLockQueueLength</tt>, as well as some associated - * <tt>protected</tt> access methods that may be useful for + * class defines methods {@code isLocked} and + * {@code getLockQueueLength}, as well as some associated + * {@code protected} access methods that may be useful for * instrumentation and monitoring. * - * <p> Serialization of this class behaves in the same way as built-in + * <p>Serialization of this class behaves in the same way as built-in * locks: a deserialized lock is in the unlocked state, regardless of * its state when serialized. * - * <p> This lock supports a maximum of 2147483648 recursive locks by - * the same thread. + * <p>This lock supports a maximum of 2147483647 recursive locks by + * the same thread. Attempts to exceed this limit result in + * {@link Error} throws from locking methods. * * @since 1.5 * @author Doug Lea - * */ public class ReentrantLock implements Lock, java.io.Serializable { private static final long serialVersionUID = 7373984872572414699L; @@ -84,32 +87,34 @@ public class ReentrantLock implements Lock, java.io.Serializable { * into fair and nonfair versions below. Uses AQS state to * represent the number of holds on the lock. */ - static abstract class Sync extends AbstractQueuedSynchronizer { - /** Current owner thread */ - transient Thread owner; + static abstract class Sync extends AbstractQueuedSynchronizer { + private static final long serialVersionUID = -5179523762034025860L; /** - * Perform {@link Lock#lock}. The main reason for subclassing + * Performs {@link Lock#lock}. The main reason for subclassing * is to allow fast path for nonfair version. */ abstract void lock(); - /** - * Perform non-fair tryLock. tryAcquire is + /** + * Performs non-fair tryLock. tryAcquire is * implemented in subclasses, but both need nonfair - * try for trylock method + * try for trylock method. */ - final boolean nonfairTryAcquire(int acquires) { + final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { - owner = current; + setExclusiveOwnerThread(current); return true; } } - else if (current == owner) { - setState(c+acquires); + else if (current == getExclusiveOwnerThread()) { + int nextc = c + acquires; + if (nextc < 0) // overflow + throw new Error("Maximum lock count exceeded"); + setState(nextc); return true; } return false; @@ -117,19 +122,21 @@ public class ReentrantLock implements Lock, java.io.Serializable { protected final boolean tryRelease(int releases) { int c = getState() - releases; - if (Thread.currentThread() != owner) + if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; - owner = null; + setExclusiveOwnerThread(null); } setState(c); return free; } protected final boolean isHeldExclusively() { - return getState() != 0 && owner == Thread.currentThread(); + // While we must in general read state before owner, + // we don't need to do so to check if current thread is owner + return getExclusiveOwnerThread() == Thread.currentThread(); } final ConditionObject newCondition() { @@ -139,23 +146,19 @@ public class ReentrantLock implements Lock, java.io.Serializable { // Methods relayed from outer class final Thread getOwner() { - int c = getState(); - Thread o = owner; - return (c == 0)? null : o; + return getState() == 0 ? null : getExclusiveOwnerThread(); } - + final int getHoldCount() { - int c = getState(); - Thread o = owner; - return (o == Thread.currentThread())? c : 0; + return isHeldExclusively() ? getState() : 0; } - + final boolean isLocked() { return getState() != 0; } /** - * Reconstitute this lock instance from a stream + * Reconstitutes this lock instance from a stream. * @param s the stream */ private void readObject(java.io.ObjectInputStream s) @@ -169,18 +172,20 @@ public class ReentrantLock implements Lock, java.io.Serializable { * Sync object for non-fair locks */ final static class NonfairSync extends Sync { + private static final long serialVersionUID = 7316153563782823691L; + /** - * Perform lock. Try immediate barge, backing up to normal + * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) - owner = Thread.currentThread(); + setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } - protected final boolean tryAcquire(int acquires) { + protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } } @@ -188,28 +193,32 @@ public class ReentrantLock implements Lock, java.io.Serializable { /** * Sync object for fair locks */ - final static class FairSync extends Sync { - final void lock() { - acquire(1); + final static class FairSync extends Sync { + private static final long serialVersionUID = -3000897897090466540L; + + final void lock() { + acquire(1); } /** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ - protected final boolean tryAcquire(int acquires) { + protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { - Thread first = getFirstQueuedThread(); - if ((first == null || first == current) && + if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { - owner = current; + setExclusiveOwnerThread(current); return true; } } - else if (current == owner) { - setState(c+acquires); + else if (current == getExclusiveOwnerThread()) { + int nextc = c + acquires; + if (nextc < 0) + throw new Error("Maximum lock count exceeded"); + setState(nextc); return true; } return false; @@ -217,89 +226,88 @@ public class ReentrantLock implements Lock, java.io.Serializable { } /** - * Creates an instance of <tt>ReentrantLock</tt>. - * This is equivalent to using <tt>ReentrantLock(false)</tt>. + * Creates an instance of {@code ReentrantLock}. + * This is equivalent to using {@code ReentrantLock(false)}. */ - public ReentrantLock() { + public ReentrantLock() { sync = new NonfairSync(); } /** - * Creates an instance of <tt>ReentrantLock</tt> with the + * Creates an instance of {@code ReentrantLock} with the * given fairness policy. - * @param fair true if this lock will be fair; else false + * + * @param fair {@code true} if this lock should use a fair ordering policy */ - public ReentrantLock(boolean fair) { + public ReentrantLock(boolean fair) { sync = (fair)? new FairSync() : new NonfairSync(); } /** - * Acquires the lock. + * Acquires the lock. * - * <p>Acquires the lock if it is not held by another thread and returns + * <p>Acquires the lock if it is not held by another thread and returns * immediately, setting the lock hold count to one. * - * <p>If the current thread - * already holds the lock then the hold count is incremented by one and - * the method returns immediately. + * <p>If the current thread already holds the lock then the hold + * count is incremented by one and the method returns immediately. * * <p>If the lock is held by another thread then the - * current thread becomes disabled for thread scheduling + * current thread becomes disabled for thread scheduling * purposes and lies dormant until the lock has been acquired, - * at which time the lock hold count is set to one. + * at which time the lock hold count is set to one. */ public void lock() { sync.lock(); } /** - * Acquires the lock unless the current thread is - * {@link Thread#interrupt interrupted}. + * Acquires the lock unless the current thread is + * {@linkplain Thread#interrupt interrupted}. * - * <p>Acquires the lock if it is not held by another thread and returns + * <p>Acquires the lock if it is not held by another thread and returns * immediately, setting the lock hold count to one. * - * <p>If the current thread already holds this lock then the hold count + * <p>If the current thread already holds this lock then the hold count * is incremented by one and the method returns immediately. * * <p>If the lock is held by another thread then the - * current thread becomes disabled for thread scheduling + * current thread becomes disabled for thread scheduling * purposes and lies dormant until one of two things happens: * * <ul> * * <li>The lock is acquired by the current thread; or * - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread. + * <li>Some other thread {@linkplain Thread#interrupt interrupts} the + * current thread. * * </ul> * - * <p>If the lock is acquired by the current thread then the lock hold + * <p>If the lock is acquired by the current thread then the lock hold * count is set to one. * * <p>If the current thread: * * <ul> * - * <li>has its interrupted status set on entry to this method; or + * <li>has its interrupted status set on entry to this method; or * - * <li>is {@link Thread#interrupt interrupted} while acquiring + * <li>is {@linkplain Thread#interrupt interrupted} while acquiring * the lock, * * </ul> * - * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. * - * <p>In this implementation, as this method is an explicit interruption - * point, preference is - * given to responding to the interrupt over normal or reentrant - * acquisition of the lock. + * <p>In this implementation, as this method is an explicit + * interruption point, preference is given to responding to the + * interrupt over normal or reentrant acquisition of the lock. * * @throws InterruptedException if the current thread is interrupted */ - public void lockInterruptibly() throws InterruptedException { + public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } @@ -308,43 +316,42 @@ public class ReentrantLock implements Lock, java.io.Serializable { * of invocation. * * <p>Acquires the lock if it is not held by another thread and - * returns immediately with the value <tt>true</tt>, setting the + * returns immediately with the value {@code true}, setting the * lock hold count to one. Even when this lock has been set to use a - * fair ordering policy, a call to <tt>tryLock()</tt> <em>will</em> + * fair ordering policy, a call to {@code tryLock()} <em>will</em> * immediately acquire the lock if it is available, whether or not - * other threads are currently waiting for the lock. - * This "barging" behavior can be useful in certain + * other threads are currently waiting for the lock. + * This "barging" behavior can be useful in certain * circumstances, even though it breaks fairness. If you want to honor - * the fairness setting for this lock, then use + * the fairness setting for this lock, then use * {@link #tryLock(long, TimeUnit) tryLock(0, TimeUnit.SECONDS) } * which is almost equivalent (it also detects interruption). * - * <p> If the current thread - * already holds this lock then the hold count is incremented by one and - * the method returns <tt>true</tt>. + * <p> If the current thread already holds this lock then the hold + * count is incremented by one and the method returns {@code true}. * - * <p>If the lock is held by another thread then this method will return - * immediately with the value <tt>false</tt>. + * <p>If the lock is held by another thread then this method will return + * immediately with the value {@code false}. * - * @return <tt>true</tt> if the lock was free and was acquired by the - * current thread, or the lock was already held by the current thread; and - * <tt>false</tt> otherwise. + * @return {@code true} if the lock was free and was acquired by the + * current thread, or the lock was already held by the current + * thread; and {@code false} otherwise */ public boolean tryLock() { return sync.nonfairTryAcquire(1); } /** - * Acquires the lock if it is not held by another thread within the given - * waiting time and the current thread has not been - * {@link Thread#interrupt interrupted}. + * Acquires the lock if it is not held by another thread within the given + * waiting time and the current thread has not been + * {@linkplain Thread#interrupt interrupted}. * - * <p>Acquires the lock if it is not held by another thread and returns - * immediately with the value <tt>true</tt>, setting the lock hold count + * <p>Acquires the lock if it is not held by another thread and returns + * immediately with the value {@code true}, setting the lock hold count * to one. If this lock has been set to use a fair ordering policy then * an available lock <em>will not</em> be acquired if any other threads * are waiting for the lock. This is in contrast to the {@link #tryLock()} - * method. If you want a timed <tt>tryLock</tt> that does permit barging on + * method. If you want a timed {@code tryLock} that does permit barging on * a fair lock then combine the timed and un-timed forms together: * * <pre>if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... } @@ -352,60 +359,56 @@ public class ReentrantLock implements Lock, java.io.Serializable { * * <p>If the current thread * already holds this lock then the hold count is incremented by one and - * the method returns <tt>true</tt>. + * the method returns {@code true}. * * <p>If the lock is held by another thread then the - * current thread becomes disabled for thread scheduling + * current thread becomes disabled for thread scheduling * purposes and lies dormant until one of three things happens: * * <ul> * * <li>The lock is acquired by the current thread; or * - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + * <li>Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or * * <li>The specified waiting time elapses * * </ul> * - * <p>If the lock is acquired then the value <tt>true</tt> is returned and + * <p>If the lock is acquired then the value {@code true} is returned and * the lock hold count is set to one. * * <p>If the current thread: * * <ul> * - * <li>has its interrupted status set on entry to this method; or + * <li>has its interrupted status set on entry to this method; or * - * <li>is {@link Thread#interrupt interrupted} while acquiring - * the lock, + * <li>is {@linkplain Thread#interrupt interrupted} while + * acquiring the lock, * * </ul> - * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. * - * <p>If the specified waiting time elapses then the value <tt>false</tt> - * is returned. - * If the time is - * less than or equal to zero, the method will not wait at all. + * <p>If the specified waiting time elapses then the value {@code false} + * is returned. If the time is less than or equal to zero, the method + * will not wait at all. * - * <p>In this implementation, as this method is an explicit interruption - * point, preference is - * given to responding to the interrupt over normal or reentrant - * acquisition of the lock, and over reporting the elapse of the waiting - * time. + * <p>In this implementation, as this method is an explicit + * interruption point, preference is given to responding to the + * interrupt over normal or reentrant acquisition of the lock, and + * over reporting the elapse of the waiting time. * * @param timeout the time to wait for the lock * @param unit the time unit of the timeout argument - * - * @return <tt>true</tt> if the lock was free and was acquired by the - * current thread, or the lock was already held by the current thread; and - * <tt>false</tt> if the waiting time elapsed before the lock could be - * acquired. - * + * @return {@code true} if the lock was free and was acquired by the + * current thread, or the lock was already held by the current + * thread; and {@code false} if the waiting time elapsed before + * the lock could be acquired * @throws InterruptedException if the current thread is interrupted - * @throws NullPointerException if unit is null + * @throws NullPointerException if the time unit is null * */ public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { @@ -413,22 +416,22 @@ public class ReentrantLock implements Lock, java.io.Serializable { } /** - * Attempts to release this lock. + * Attempts to release this lock. + * + * <p>If the current thread is the holder of this lock then the hold + * count is decremented. If the hold count is now zero then the lock + * is released. If the current thread is not the holder of this + * lock then {@link IllegalMonitorStateException} is thrown. * - * <p>If the current thread is the - * holder of this lock then the hold count is decremented. If the - * hold count is now zero then the lock is released. If the - * current thread is not the holder of this lock then {@link - * IllegalMonitorStateException} is thrown. * @throws IllegalMonitorStateException if the current thread does not - * hold this lock. + * hold this lock */ public void unlock() { sync.release(1); } /** - * Returns a {@link Condition} instance for use with this + * Returns a {@link Condition} instance for use with this * {@link Lock} instance. * * <p>The returned {@link Condition} instance supports the same @@ -440,28 +443,28 @@ public class ReentrantLock implements Lock, java.io.Serializable { * <ul> * * <li>If this lock is not held when any of the {@link Condition} - * {@link Condition#await() waiting} or {@link Condition#signal - * signalling} methods are called, then an {@link + * {@linkplain Condition#await() waiting} or {@linkplain + * Condition#signal signalling} methods are called, then an {@link * IllegalMonitorStateException} is thrown. * - * <li>When the condition {@link Condition#await() waiting} + * <li>When the condition {@linkplain Condition#await() waiting} * methods are called the lock is released and, before they * return, the lock is reacquired and the lock hold count restored * to what it was when the method was called. * - * <li>If a thread is {@link Thread#interrupt interrupted} while - * waiting then the wait will terminate, an {@link + * <li>If a thread is {@linkplain Thread#interrupt interrupted} + * while waiting then the wait will terminate, an {@link * InterruptedException} will be thrown, and the thread's * interrupted status will be cleared. * - * <li> Waiting threads are signalled in FIFO order + * <li> Waiting threads are signalled in FIFO order. * * <li>The ordering of lock reacquisition for threads returning * from waiting methods is the same as for threads initially * acquiring the lock, which is in the default case not specified, * but for <em>fair</em> locks favors those threads that have been * waiting the longest. - * + * * </ul> * * @return the Condition object @@ -473,7 +476,7 @@ public class ReentrantLock implements Lock, java.io.Serializable { /** * Queries the number of holds on this lock by the current thread. * - * <p>A thread has a hold on a lock for each lock action that is not + * <p>A thread has a hold on a lock for each lock action that is not * matched by an unlock action. * * <p>The hold count information is typically only used for testing and @@ -484,8 +487,8 @@ public class ReentrantLock implements Lock, java.io.Serializable { * <pre> * class X { * ReentrantLock lock = new ReentrantLock(); - * // ... - * public void m() { + * // ... + * public void m() { * assert lock.getHoldCount() == 0; * lock.lock(); * try { @@ -498,7 +501,7 @@ public class ReentrantLock implements Lock, java.io.Serializable { * </pre> * * @return the number of holds on this lock by the current thread, - * or zero if this lock is not held by the current thread. + * or zero if this lock is not held by the current thread */ public int getHoldCount() { return sync.getHoldCount(); @@ -517,7 +520,7 @@ public class ReentrantLock implements Lock, java.io.Serializable { * ReentrantLock lock = new ReentrantLock(); * // ... * - * public void m() { + * public void m() { * assert lock.isHeldByCurrentThread(); * // ... method body * } @@ -532,7 +535,7 @@ public class ReentrantLock implements Lock, java.io.Serializable { * ReentrantLock lock = new ReentrantLock(); * // ... * - * public void m() { + * public void m() { * assert !lock.isHeldByCurrentThread(); * lock.lock(); * try { @@ -543,8 +546,9 @@ public class ReentrantLock implements Lock, java.io.Serializable { * } * } * </pre> - * @return <tt>true</tt> if current thread holds this lock and - * <tt>false</tt> otherwise. + * + * @return {@code true} if current thread holds this lock and + * {@code false} otherwise */ public boolean isHeldByCurrentThread() { return sync.isHeldExclusively(); @@ -552,47 +556,53 @@ public class ReentrantLock implements Lock, java.io.Serializable { /** * Queries if this lock is held by any thread. This method is - * designed for use in monitoring of the system state, + * designed for use in monitoring of the system state, * not for synchronization control. - * @return <tt>true</tt> if any thread holds this lock and - * <tt>false</tt> otherwise. + * + * @return {@code true} if any thread holds this lock and + * {@code false} otherwise */ public boolean isLocked() { return sync.isLocked(); } /** - * Returns true if this lock has fairness set true. - * @return true if this lock has fairness set true. + * Returns {@code true} if this lock has fairness set true. + * + * @return {@code true} if this lock has fairness set true */ public final boolean isFair() { return sync instanceof FairSync; } /** - * Returns the thread that currently owns the exclusive lock, or - * <tt>null</tt> if not owned. Note that the owner may be - * momentarily <tt>null</tt> even if there are threads trying to - * acquire the lock but have not yet done so. This method is - * designed to facilitate construction of subclasses that provide - * more extensive lock monitoring facilities. - * @return the owner, or <tt>null</tt> if not owned. + * Returns the thread that currently owns this lock, or + * {@code null} if not owned. When this method is called by a + * thread that is not the owner, the return value reflects a + * best-effort approximation of current lock status. For example, + * the owner may be momentarily {@code null} even if there are + * threads trying to acquire the lock but have not yet done so. + * This method is designed to facilitate construction of + * subclasses that provide more extensive lock monitoring + * facilities. + * + * @return the owner, or {@code null} if not owned */ protected Thread getOwner() { return sync.getOwner(); } /** - * Queries whether any threads are waiting to acquire. Note that - * because cancellations may occur at any time, a <tt>true</tt> + * Queries whether any threads are waiting to acquire this lock. Note that + * because cancellations may occur at any time, a {@code true} * return does not guarantee that any other thread will ever - * acquire. This method is designed primarily for use in + * acquire this lock. This method is designed primarily for use in * monitoring of the system state. * - * @return true if there may be other threads waiting to acquire - * the lock. + * @return {@code true} if there may be other threads waiting to + * acquire the lock */ - public final boolean hasQueuedThreads() { + public final boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } @@ -600,26 +610,27 @@ public class ReentrantLock implements Lock, java.io.Serializable { /** * Queries whether the given thread is waiting to acquire this * lock. Note that because cancellations may occur at any time, a - * <tt>true</tt> return does not guarantee that this thread - * will ever acquire. This method is designed primarily for use + * {@code true} return does not guarantee that this thread + * will ever acquire this lock. This method is designed primarily for use * in monitoring of the system state. * * @param thread the thread - * @return true if the given thread is queued waiting for this lock. - * @throws NullPointerException if thread is null + * @return {@code true} if the given thread is queued waiting for this lock + * @throws NullPointerException if the thread is null */ - public final boolean hasQueuedThread(Thread thread) { + public final boolean hasQueuedThread(Thread thread) { return sync.isQueued(thread); } /** * Returns an estimate of the number of threads waiting to - * acquire. The value is only an estimate because the number of + * acquire this lock. The value is only an estimate because the number of * threads may change dynamically while this method traverses * internal data structures. This method is designed for use in * monitoring of the system state, not for synchronization * control. + * * @return the estimated number of threads waiting for this lock */ public final int getQueueLength() { @@ -628,12 +639,13 @@ public class ReentrantLock implements Lock, java.io.Serializable { /** * Returns a collection containing threads that may be waiting to - * acquire. Because the actual set of threads may change + * acquire this lock. Because the actual set of threads may change * dynamically while constructing this result, the returned * collection is only a best-effort estimate. The elements of the * returned collection are in no particular order. This method is * designed to facilitate construction of subclasses that provide * more extensive monitoring facilities. + * * @return the collection of threads */ protected Collection<Thread> getQueuedThreads() { @@ -643,18 +655,18 @@ public class ReentrantLock implements Lock, java.io.Serializable { /** * Queries whether any threads are waiting on the given condition * associated with this lock. Note that because timeouts and - * interrupts may occur at any time, a <tt>true</tt> return does - * not guarantee that a future <tt>signal</tt> will awaken any + * interrupts may occur at any time, a {@code true} return does + * not guarantee that a future {@code signal} will awaken any * threads. This method is designed primarily for use in * monitoring of the system state. + * * @param condition the condition - * @return <tt>true</tt> if there are any waiting threads. - * @throws IllegalMonitorStateException if this lock - * is not held + * @return {@code true} if there are any waiting threads + * @throws IllegalMonitorStateException if this lock is not held * @throws IllegalArgumentException if the given condition is - * not associated with this lock - * @throws NullPointerException if condition null - */ + * not associated with this lock + * @throws NullPointerException if the condition is null + */ public boolean hasWaiters(Condition condition) { if (condition == null) throw new NullPointerException(); @@ -670,14 +682,14 @@ public class ReentrantLock implements Lock, java.io.Serializable { * serves only as an upper bound on the actual number of waiters. * This method is designed for use in monitoring of the system * state, not for synchronization control. + * * @param condition the condition - * @return the estimated number of waiting threads. - * @throws IllegalMonitorStateException if this lock - * is not held + * @return the estimated number of waiting threads + * @throws IllegalMonitorStateException if this lock is not held * @throws IllegalArgumentException if the given condition is - * not associated with this lock - * @throws NullPointerException if condition null - */ + * not associated with this lock + * @throws NullPointerException if the condition is null + */ public int getWaitQueueLength(Condition condition) { if (condition == null) throw new NullPointerException(); @@ -695,13 +707,13 @@ public class ReentrantLock implements Lock, java.io.Serializable { * are in no particular order. This method is designed to * facilitate construction of subclasses that provide more * extensive condition monitoring facilities. + * * @param condition the condition * @return the collection of threads - * @throws IllegalMonitorStateException if this lock - * is not held + * @throws IllegalMonitorStateException if this lock is not held * @throws IllegalArgumentException if the given condition is - * not associated with this lock - * @throws NullPointerException if condition null + * not associated with this lock + * @throws NullPointerException if the condition is null */ protected Collection<Thread> getWaitingThreads(Condition condition) { if (condition == null) @@ -712,16 +724,17 @@ public class ReentrantLock implements Lock, java.io.Serializable { } /** - * Returns a string identifying this lock, as well as its lock - * state. The state, in brackets, includes either the String - * "Unlocked" or the String "Locked by" - * followed by the {@link Thread#getName} of the owning thread. - * @return a string identifying this lock, as well as its lock state. + * Returns a string identifying this lock, as well as its lock state. + * The state, in brackets, includes either the String {@code "Unlocked"} + * or the String {@code "Locked by"} followed by the + * {@linkplain Thread#getName name} of the owning thread. + * + * @return a string identifying this lock, as well as its lock state */ public String toString() { - Thread owner = sync.getOwner(); - return super.toString() + ((owner == null) ? + Thread o = sync.getOwner(); + return super.toString() + ((o == null) ? "[Unlocked]" : - "[Locked by thread " + owner.getName() + "]"); + "[Locked by thread " + o.getName() + "]"); } } diff --git a/concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java b/concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java index 95fc7af..c923944 100644 --- a/concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java +++ b/concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java @@ -19,32 +19,60 @@ import java.util.*; * * <p> This class does not impose a reader or writer preference * ordering for lock access. However, it does support an optional - * <em>fairness</em> policy. When constructed as fair, threads - * contend for entry using an approximately arrival-order policy. When - * the write lock is released either the longest-waiting single writer - * will be assigned the write lock, or if there is a reader waiting - * longer than any writer, the set of readers will be assigned the - * read lock. When constructed as non-fair, the order of entry to the - * lock need not be in arrival order. In either case, if readers are - * active and a writer enters the lock then no subsequent readers will - * be granted the read lock until after that writer has acquired and - * released the write lock. - * + * <em>fairness</em> policy. + * + * <dl> + * <dt><b><i>Non-fair mode (default)</i></b> + * <dd>When constructed as non-fair (the default), the order of entry + * to the read and write lock is unspecified, subject to reentrancy + * constraints. A nonfair lock that is continuously contended may + * indefinitely postpone one or more reader or writer threads, but + * will normally have higher throughput than a fair lock. + * <p> + * + * <dt><b><i>Fair mode</i></b> + * <dd> When constructed as fair, threads contend for entry using an + * approximately arrival-order policy. When the currently held lock + * is released either the longest-waiting single writer thread will + * be assigned the write lock, or if there is a group of reader threads + * waiting longer than all waiting writer threads, that group will be + * assigned the read lock. + * + * <p>A thread that tries to acquire a fair read lock (non-reentrantly) + * will block if either the write lock is held, or there is a waiting + * writer thread. The thread will not acquire the read lock until + * after the oldest currently waiting writer thread has acquired and + * released the write lock. Of course, if a waiting writer abandons + * its wait, leaving one or more reader threads as the longest waiters + * in the queue with the write lock free, then those readers will be + * assigned the read lock. + * + * <p>A thread that tries to acquire a fair write lock (non-reentrantly) + * will block unless both the read lock and write lock are free (which + * implies there are no waiting threads). (Note that the non-blocking + * {@link ReadLock#tryLock()} and {@link WriteLock#tryLock()} methods + * do not honor this fair setting and will acquire the lock if it is + * possible, regardless of waiting threads.) + * <p> + * </dl> + * * <li><b>Reentrancy</b> + * * <p>This lock allows both readers and writers to reacquire read or - * write locks in the style of a {@link ReentrantLock}. Readers are not - * allowed until all write locks held by the writing thread have been - * released. - * <p>Additionally, a writer can acquire the read lock - but not vice-versa. - * Among other applications, reentrancy can be useful when - * write locks are held during calls or callbacks to methods that - * perform reads under read locks. - * If a reader tries to acquire the write lock it will never succeed. - * + * write locks in the style of a {@link ReentrantLock}. Non-reentrant + * readers are not allowed until all write locks held by the writing + * thread have been released. + * + * <p>Additionally, a writer can acquire the read lock, but not + * vice-versa. Among other applications, reentrancy can be useful + * when write locks are held during calls or callbacks to methods that + * perform reads under read locks. If a reader tries to acquire the + * write lock it will never succeed. + * * <li><b>Lock downgrading</b> * <p>Reentrancy also allows downgrading from the write lock to a read lock, * by acquiring the write lock, then the read lock and then releasing the - * write lock. However, upgrading from a read lock to the write lock, is + * write lock. However, upgrading from a read lock to the write lock is * <b>not</b> possible. * * <li><b>Interruption of lock acquisition</b> @@ -53,96 +81,105 @@ import java.util.*; * * <li><b>{@link Condition} support</b> * <p>The write lock provides a {@link Condition} implementation that - * behaves in the same way, with respect to the write lock, as the + * behaves in the same way, with respect to the write lock, as the * {@link Condition} implementation provided by * {@link ReentrantLock#newCondition} does for {@link ReentrantLock}. * This {@link Condition} can, of course, only be used with the write lock. + * * <p>The read lock does not support a {@link Condition} and - * <tt>readLock().newCondition()</tt> throws - * <tt>UnsupportedOperationException</tt>. + * {@code readLock().newCondition()} throws + * {@code UnsupportedOperationException}. * * <li><b>Instrumentation</b> - * <P> This class supports methods to determine whether locks + * <p>This class supports methods to determine whether locks * are held or contended. These methods are designed for monitoring * system state, not for synchronization control. * </ul> * - * <p> Serialization of this class behaves in the same way as built-in + * <p>Serialization of this class behaves in the same way as built-in * locks: a deserialized lock is in the unlocked state, regardless of * its state when serialized. * - * <p><b>Sample usages</b>. Here is a code sketch showing how to exploit - * reentrancy to perform lock downgrading after updating a cache (exception - * handling is elided for simplicity): - * <pre> + * <p><b>Sample usages</b>. Here is a code sketch showing how to perform + * lock downgrading after updating a cache (exception handling is + * particularly tricky when handling multiple locks in a non-nested + * fashion): + * + * <pre> {@code * class CachedData { * Object data; * volatile boolean cacheValid; - * ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); + * final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); * * void processCachedData() { * rwl.readLock().lock(); * if (!cacheValid) { - * // upgrade lock manually - * rwl.readLock().unlock(); // must unlock first to obtain writelock + * // Must release read lock before acquiring write lock + * rwl.readLock().unlock(); * rwl.writeLock().lock(); - * if (!cacheValid) { // recheck - * data = ... - * cacheValid = true; + * try { + * // Recheck state because another thread might have + * // acquired write lock and changed state before we did. + * if (!cacheValid) { + * data = ... + * cacheValid = true; + * } + * // Downgrade by acquiring read lock before releasing write lock + * rwl.readLock().lock(); + * } finally { + * rwl.writeLock().unlock(); // Unlock write, still hold read * } - * // downgrade lock - * rwl.readLock().lock(); // reacquire read without giving up write lock - * rwl.writeLock().unlock(); // unlock write, still hold read * } * - * use(data); - * rwl.readLock().unlock(); + * try { + * use(data); + * } finally { + * rwl.readLock().unlock(); + * } * } - * } - * </pre> + * }}</pre> * * ReentrantReadWriteLocks can be used to improve concurrency in some * uses of some kinds of Collections. This is typically worthwhile * only when the collections are expected to be large, accessed by * more reader threads than writer threads, and entail operations with * overhead that outweighs synchronization overhead. For example, here - * is a class using a TreeMap that is expected to be large and + * is a class using a TreeMap that is expected to be large and * concurrently accessed. * - * <pre> + * <pre>{@code * class RWDictionary { - * private final Map<String, Data> m = new TreeMap<String, Data>(); + * private final Map<String, Data> m = new TreeMap<String, Data>(); * private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); * private final Lock r = rwl.readLock(); * private final Lock w = rwl.writeLock(); * * public Data get(String key) { - * r.lock(); try { return m.get(key); } finally { r.unlock(); } + * r.lock(); + * try { return m.get(key); } + * finally { r.unlock(); } * } * public String[] allKeys() { - * r.lock(); try { return m.keySet().toArray(); } finally { r.unlock(); } + * r.lock(); + * try { return m.keySet().toArray(); } + * finally { r.unlock(); } * } * public Data put(String key, Data value) { - * w.lock(); try { return m.put(key, value); } finally { w.unlock(); } + * w.lock(); + * try { return m.put(key, value); } + * finally { w.unlock(); } * } * public void clear() { - * w.lock(); try { m.clear(); } finally { w.unlock(); } + * w.lock(); + * try { m.clear(); } + * finally { w.unlock(); } * } - * } - * </pre> - * + * }}</pre> * * <h3>Implementation Notes</h3> * - * <p>A reentrant write lock intrinsically defines an owner and can - * only be released by the thread that acquired it. In contrast, in - * this implementation, the read lock has no concept of ownership, and - * there is no requirement that the thread releasing a read lock is - * the same as the one that acquired it. However, this property is - * not guaranteed to hold in future implementations of this class. - * - * <p> This lock supports a maximum of 65536 recursive write locks - * and 65536 read locks. Attempts to exceed these limits result in + * <p>This lock supports a maximum of 65535 recursive write locks + * and 65535 read locks. Attempts to exceed these limits result in * {@link Error} throws from locking methods. * * @since 1.5 @@ -156,26 +193,24 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab /** Inner class providing writelock */ private final ReentrantReadWriteLock.WriteLock writerLock; /** Performs all synchronization mechanics */ - private final Sync sync; + final Sync sync; /** - * Creates a new <tt>ReentrantReadWriteLock</tt> with - * default ordering properties. + * Creates a new {@code ReentrantReadWriteLock} with + * default (nonfair) ordering properties. */ public ReentrantReadWriteLock() { - sync = new NonfairSync(); - readerLock = new ReadLock(this); - writerLock = new WriteLock(this); + this(false); } /** - * Creates a new <tt>ReentrantReadWriteLock</tt> with + * Creates a new {@code ReentrantReadWriteLock} with * the given fairness policy. * - * @param fair true if this lock should use a fair ordering policy + * @param fair {@code true} if this lock should use a fair ordering policy */ public ReentrantReadWriteLock(boolean fair) { - sync = (fair)? new FairSync() : new NonfairSync(); + sync = fair ? new FairSync() : new NonfairSync(); readerLock = new ReadLock(this); writerLock = new WriteLock(this); } @@ -183,129 +218,409 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; } public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; } - /* - * Read vs write count extraction constants and functions. - * Lock state is logically divided into two shorts: The lower - * one representing the exclusive (writer) lock hold count, - * and the upper the shared (reader) hold count. - */ - - static final int SHARED_SHIFT = 16; - static final int SHARED_UNIT = (1 << SHARED_SHIFT); - static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; - - /** Returns the number of shared holds represented in count */ - static int sharedCount(int c) { return c >>> SHARED_SHIFT; } - /** Returns the number of exclusive holds represented in count */ - static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; } - - /** + /** * Synchronization implementation for ReentrantReadWriteLock. * Subclassed into fair and nonfair versions. */ - abstract static class Sync extends AbstractQueuedSynchronizer { - /** Current (exclusive) owner thread */ - transient Thread owner; + static abstract class Sync extends AbstractQueuedSynchronizer { + private static final long serialVersionUID = 6317671515068378041L; + + /* + * Read vs write count extraction constants and functions. + * Lock state is logically divided into two unsigned shorts: + * The lower one representing the exclusive (writer) lock hold count, + * and the upper the shared (reader) hold count. + */ + + static final int SHARED_SHIFT = 16; + static final int SHARED_UNIT = (1 << SHARED_SHIFT); + static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; + static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; + + /** Returns the number of shared holds represented in count */ + static int sharedCount(int c) { return c >>> SHARED_SHIFT; } + /** Returns the number of exclusive holds represented in count */ + static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; } + + /** + * A counter for per-thread read hold counts. + * Maintained as a ThreadLocal; cached in cachedHoldCounter + */ + static final class HoldCounter { + int count = 0; + // Use id, not reference, to avoid garbage retention + final long tid = Thread.currentThread().getId(); + } + + /** + * ThreadLocal subclass. Easiest to explicitly define for sake + * of deserialization mechanics. + */ + static final class ThreadLocalHoldCounter + extends ThreadLocal<HoldCounter> { + public HoldCounter initialValue() { + return new HoldCounter(); + } + } + + /** + * The number of reentrant read locks held by current thread. + * Initialized only in constructor and readObject. + * Removed whenever a thread's read hold count drops to 0. + */ + private transient ThreadLocalHoldCounter readHolds; + + /** + * The hold count of the last thread to successfully acquire + * readLock. This saves ThreadLocal lookup in the common case + * where the next thread to release is the last one to + * acquire. This is non-volatile since it is just used + * as a heuristic, and would be great for threads to cache. + * + * <p>Can outlive the Thread for which it is caching the read + * hold count, but avoids garbage retention by not retaining a + * reference to the Thread. + * + * <p>Accessed via a benign data race; relies on the memory + * model's final field and out-of-thin-air guarantees. + */ + private transient HoldCounter cachedHoldCounter; + + /** + * firstReader is the first thread to have acquired the read lock. + * firstReaderHoldCount is firstReader's hold count. + * + * <p>More precisely, firstReader is the unique thread that last + * changed the shared count from 0 to 1, and has not released the + * read lock since then; null if there is no such thread. + * + * <p>Cannot cause garbage retention unless the thread terminated + * without relinquishing its read locks, since tryReleaseShared + * sets it to null. + * + * <p>Accessed via a benign data race; relies on the memory + * model's out-of-thin-air guarantees for references. + * + * <p>This allows tracking of read holds for uncontended read + * locks to be very cheap. + */ + private transient Thread firstReader = null; + private transient int firstReaderHoldCount; + + Sync() { + readHolds = new ThreadLocalHoldCounter(); + setState(getState()); // ensures visibility of readHolds + } + + /* + * Acquires and releases use the same code for fair and + * nonfair locks, but differ in whether/how they allow barging + * when queues are non-empty. + */ /** - * Perform write lock. Allows fast path in non-fair version. + * Returns true if the current thread, when trying to acquire + * the read lock, and otherwise eligible to do so, should block + * because of policy for overtaking other waiting threads. */ - abstract void wlock(); + abstract boolean readerShouldBlock(); - /** - * Perform non-fair tryLock for write. tryAcquire is - * implemented in subclasses, but both versions need nonfair - * try for trylock method + /** + * Returns true if the current thread, when trying to acquire + * the write lock, and otherwise eligible to do so, should block + * because of policy for overtaking other waiting threads. */ - final boolean nonfairTryAcquire(int acquires) { - // mask out readlocks if called from condition methods - acquires = exclusiveCount(acquires); + abstract boolean writerShouldBlock(); + + /* + * Note that tryRelease and tryAcquire can be called by + * Conditions. So it is possible that their arguments contain + * both read and write holds that are all released during a + * condition wait and re-established in tryAcquire. + */ + + protected final boolean tryRelease(int releases) { + if (!isHeldExclusively()) + throw new IllegalMonitorStateException(); + int nextc = getState() - releases; + boolean free = exclusiveCount(nextc) == 0; + if (free) + setExclusiveOwnerThread(null); + setState(nextc); + return free; + } + + protected final boolean tryAcquire(int acquires) { + /* + * Walkthrough: + * 1. If read count nonzero or write count nonzero + * and owner is a different thread, fail. + * 2. If count would saturate, fail. (This can only + * happen if count is already nonzero.) + * 3. Otherwise, this thread is eligible for lock if + * it is either a reentrant acquire or + * queue policy allows it. If so, update state + * and set owner. + */ Thread current = Thread.currentThread(); int c = getState(); int w = exclusiveCount(c); - if (w + acquires >= SHARED_UNIT) - throw new Error("Maximum lock count exceeded"); - if (c != 0 && (w == 0 || current != owner)) - return false; - if (!compareAndSetState(c, c + acquires)) + if (c != 0) { + // (Note: if c != 0 and w == 0 then shared count != 0) + if (w == 0 || current != getExclusiveOwnerThread()) + return false; + if (w + exclusiveCount(acquires) > MAX_COUNT) + throw new Error("Maximum lock count exceeded"); + // Reentrant acquire + setState(c + acquires); + return true; + } + if (writerShouldBlock() || + !compareAndSetState(c, c + acquires)) return false; - owner = current; + setExclusiveOwnerThread(current); return true; } - /** - * Perform nonfair tryLock for read. + protected final boolean tryReleaseShared(int unused) { + Thread current = Thread.currentThread(); + if (firstReader == current) { + // assert firstReaderHoldCount > 0; + if (firstReaderHoldCount == 1) + firstReader = null; + else + firstReaderHoldCount--; + } else { + HoldCounter rh = cachedHoldCounter; + if (rh == null || rh.tid != current.getId()) + rh = readHolds.get(); + int count = rh.count; + if (count <= 1) { + readHolds.remove(); + if (count <= 0) + throw unmatchedUnlockException(); + } + --rh.count; + } + for (;;) { + int c = getState(); + int nextc = c - SHARED_UNIT; + if (compareAndSetState(c, nextc)) + // Releasing the read lock has no effect on readers, + // but it may allow waiting writers to proceed if + // both read and write locks are now free. + return nextc == 0; + } + } + + private IllegalMonitorStateException unmatchedUnlockException() { + return new IllegalMonitorStateException( + "attempt to unlock read lock, not locked by current thread"); + } + + protected final int tryAcquireShared(int unused) { + /* + * Walkthrough: + * 1. If write lock held by another thread, fail. + * 2. Otherwise, this thread is eligible for + * lock wrt state, so ask if it should block + * because of queue policy. If not, try + * to grant by CASing state and updating count. + * Note that step does not check for reentrant + * acquires, which is postponed to full version + * to avoid having to check hold count in + * the more typical non-reentrant case. + * 3. If step 2 fails either because thread + * apparently not eligible or CAS fails or count + * saturated, chain to version with full retry loop. + */ + Thread current = Thread.currentThread(); + int c = getState(); + if (exclusiveCount(c) != 0 && + getExclusiveOwnerThread() != current) + return -1; + int r = sharedCount(c); + if (!readerShouldBlock() && + r < MAX_COUNT && + compareAndSetState(c, c + SHARED_UNIT)) { + if (r == 0) { + firstReader = current; + firstReaderHoldCount = 1; + } else if (firstReader == current) { + firstReaderHoldCount++; + } else { + HoldCounter rh = cachedHoldCounter; + if (rh == null || rh.tid != current.getId()) + cachedHoldCounter = rh = readHolds.get(); + else if (rh.count == 0) + readHolds.set(rh); + rh.count++; + } + return 1; + } + return fullTryAcquireShared(current); + } + + /** + * Full version of acquire for reads, that handles CAS misses + * and reentrant reads not dealt with in tryAcquireShared. */ - final int nonfairTryAcquireShared(int acquires) { + final int fullTryAcquireShared(Thread current) { + /* + * This code is in part redundant with that in + * tryAcquireShared but is simpler overall by not + * complicating tryAcquireShared with interactions between + * retries and lazily reading hold counts. + */ + HoldCounter rh = null; for (;;) { int c = getState(); - int nextc = c + (acquires << SHARED_SHIFT); - if (nextc < c) + if (exclusiveCount(c) != 0) { + if (getExclusiveOwnerThread() != current) + return -1; + // else we hold the exclusive lock; blocking here + // would cause deadlock. + } else if (readerShouldBlock()) { + // Make sure we're not acquiring read lock reentrantly + if (firstReader == current) { + // assert firstReaderHoldCount > 0; + } else { + if (rh == null) { + rh = cachedHoldCounter; + if (rh == null || rh.tid != current.getId()) { + rh = readHolds.get(); + if (rh.count == 0) + readHolds.remove(); + } + } + if (rh.count == 0) + return -1; + } + } + if (sharedCount(c) == MAX_COUNT) throw new Error("Maximum lock count exceeded"); - if (exclusiveCount(c) != 0 && - owner != Thread.currentThread()) - return -1; - if (compareAndSetState(c, nextc)) + if (compareAndSetState(c, c + SHARED_UNIT)) { + if (sharedCount(c) == 0) { + firstReader = current; + firstReaderHoldCount = 1; + } else if (firstReader == current) { + firstReaderHoldCount++; + } else { + if (rh == null) + rh = cachedHoldCounter; + if (rh == null || rh.tid != current.getId()) + rh = readHolds.get(); + else if (rh.count == 0) + readHolds.set(rh); + rh.count++; + cachedHoldCounter = rh; // cache for release + } return 1; - // Recheck count if lost CAS + } } } - protected final boolean tryRelease(int releases) { + /** + * Performs tryLock for write, enabling barging in both modes. + * This is identical in effect to tryAcquire except for lack + * of calls to writerShouldBlock. + */ + final boolean tryWriteLock() { Thread current = Thread.currentThread(); int c = getState(); - if (owner != current) - throw new IllegalMonitorStateException(); - int nextc = c - releases; - boolean free = false; - if (exclusiveCount(c) == releases) { - free = true; - owner = null; + if (c != 0) { + int w = exclusiveCount(c); + if (w == 0 || current != getExclusiveOwnerThread()) + return false; + if (w == MAX_COUNT) + throw new Error("Maximum lock count exceeded"); } - setState(nextc); - return free; + if (!compareAndSetState(c, c + 1)) + return false; + setExclusiveOwnerThread(current); + return true; } - protected final boolean tryReleaseShared(int releases) { + /** + * Performs tryLock for read, enabling barging in both modes. + * This is identical in effect to tryAcquireShared except for + * lack of calls to readerShouldBlock. + */ + final boolean tryReadLock() { + Thread current = Thread.currentThread(); for (;;) { int c = getState(); - int nextc = c - (releases << SHARED_SHIFT); - if (nextc < 0) - throw new IllegalMonitorStateException(); - if (compareAndSetState(c, nextc)) - return nextc == 0; + if (exclusiveCount(c) != 0 && + getExclusiveOwnerThread() != current) + return false; + int r = sharedCount(c); + if (r == MAX_COUNT) + throw new Error("Maximum lock count exceeded"); + if (compareAndSetState(c, c + SHARED_UNIT)) { + if (r == 0) { + firstReader = current; + firstReaderHoldCount = 1; + } else if (firstReader == current) { + firstReaderHoldCount++; + } else { + HoldCounter rh = cachedHoldCounter; + if (rh == null || rh.tid != current.getId()) + cachedHoldCounter = rh = readHolds.get(); + else if (rh.count == 0) + readHolds.set(rh); + rh.count++; + } + return true; + } } } - + protected final boolean isHeldExclusively() { - return exclusiveCount(getState()) != 0 && - owner == Thread.currentThread(); + // While we must in general read state before owner, + // we don't need to do so to check if current thread is owner + return getExclusiveOwnerThread() == Thread.currentThread(); } // Methods relayed to outer class - - final ConditionObject newCondition() { - return new ConditionObject(); + + final ConditionObject newCondition() { + return new ConditionObject(); } final Thread getOwner() { - int c = exclusiveCount(getState()); - Thread o = owner; - return (c == 0)? null : o; + // Must read state before owner to ensure memory consistency + return ((exclusiveCount(getState()) == 0)? + null : + getExclusiveOwnerThread()); } - + final int getReadLockCount() { return sharedCount(getState()); } - + final boolean isWriteLocked() { return exclusiveCount(getState()) != 0; } final int getWriteHoldCount() { - int c = exclusiveCount(getState()); - Thread o = owner; - return (o == Thread.currentThread())? c : 0; + return isHeldExclusively() ? exclusiveCount(getState()) : 0; + } + + final int getReadHoldCount() { + if (getReadLockCount() == 0) + return 0; + + Thread current = Thread.currentThread(); + if (firstReader == current) + return firstReaderHoldCount; + + HoldCounter rh = cachedHoldCounter; + if (rh != null && rh.tid == current.getId()) + return rh.count; + + int count = readHolds.get().count; + if (count == 0) readHolds.remove(); + return count; } /** @@ -315,78 +630,43 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); + readHolds = new ThreadLocalHoldCounter(); setState(0); // reset to unlocked state } final int getCount() { return getState(); } } - /** + /** * Nonfair version of Sync */ final static class NonfairSync extends Sync { - protected final boolean tryAcquire(int acquires) { - return nonfairTryAcquire(acquires); + private static final long serialVersionUID = -8159625535654395037L; + final boolean writerShouldBlock() { + return false; // writers can always barge } - - protected final int tryAcquireShared(int acquires) { - return nonfairTryAcquireShared(acquires); - } - - // Use fastpath for main write lock method - final void wlock() { - if (compareAndSetState(0, 1)) - owner = Thread.currentThread(); - else - acquire(1); + final boolean readerShouldBlock() { + /* As a heuristic to avoid indefinite writer starvation, + * block if the thread that momentarily appears to be head + * of queue, if one exists, is a waiting writer. This is + * only a probabilistic effect since a new reader will not + * block if there is a waiting writer behind other enabled + * readers that have not yet drained from the queue. + */ + return apparentlyFirstQueuedIsExclusive(); } } - /** + /** * Fair version of Sync */ final static class FairSync extends Sync { - protected final boolean tryAcquire(int acquires) { - // mask out readlocks if called from condition methods - acquires = exclusiveCount(acquires); - Thread current = Thread.currentThread(); - Thread first; - int c = getState(); - int w = exclusiveCount(c); - if (w + acquires >= SHARED_UNIT) - throw new Error("Maximum lock count exceeded"); - if ((w == 0 || current != owner) && - (c != 0 || - ((first = getFirstQueuedThread()) != null && - first != current))) - return false; - if (!compareAndSetState(c, c + acquires)) - return false; - owner = current; - return true; - } - - protected final int tryAcquireShared(int acquires) { - Thread current = Thread.currentThread(); - for (;;) { - Thread first = getFirstQueuedThread(); - if (first != null && first != current) - return -1; - int c = getState(); - int nextc = c + (acquires << SHARED_SHIFT); - if (nextc < c) - throw new Error("Maximum lock count exceeded"); - if (exclusiveCount(c) != 0 && - owner != Thread.currentThread()) - return -1; - if (compareAndSetState(c, nextc)) - return 1; - // Recheck count if lost CAS - } + private static final long serialVersionUID = -2274990926593161451L; + final boolean writerShouldBlock() { + return hasQueuedPredecessors(); } - - final void wlock() { // no fast path - acquire(1); + final boolean readerShouldBlock() { + return hasQueuedPredecessors(); } } @@ -396,46 +676,47 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab public static class ReadLock implements Lock, java.io.Serializable { private static final long serialVersionUID = -5992448646407690164L; private final Sync sync; - - /** - * Constructor for use by subclasses. + + /** + * Constructor for use by subclasses + * * @param lock the outer lock object - * @throws NullPointerException if lock null + * @throws NullPointerException if the lock is null */ protected ReadLock(ReentrantReadWriteLock lock) { sync = lock.sync; } /** - * Acquires the shared lock. + * Acquires the read lock. * - * <p>Acquires the lock if it is not held exclusively by + * <p>Acquires the read lock if the write lock is not held by * another thread and returns immediately. * - * <p>If the lock is held exclusively by another thread then + * <p>If the write lock is held by another thread then * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until the lock has been acquired. + * purposes and lies dormant until the read lock has been acquired. */ - public void lock() { + public void lock() { sync.acquireShared(1); } /** - * Acquires the shared lock unless the current thread is - * {@link Thread#interrupt interrupted}. + * Acquires the read lock unless the current thread is + * {@linkplain Thread#interrupt interrupted}. * - * <p>Acquires the shared lock if it is not held exclusively + * <p>Acquires the read lock if the write lock is not held * by another thread and returns immediately. * - * <p>If the lock is held by another thread then the - * current thread becomes disabled for thread scheduling + * <p>If the write lock is held by another thread then the + * current thread becomes disabled for thread scheduling * purposes and lies dormant until one of two things happens: * * <ul> * - * <li>The lock is acquired by the current thread; or + * <li>The read lock is acquired by the current thread; or * - * <li>Some other thread {@link Thread#interrupt interrupts} + * <li>Some other thread {@linkplain Thread#interrupt interrupts} * the current thread. * * </ul> @@ -444,10 +725,10 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * * <ul> * - * <li>has its interrupted status set on entry to this method; or + * <li>has its interrupted status set on entry to this method; or * - * <li>is {@link Thread#interrupt interrupted} while acquiring - * the lock, + * <li>is {@linkplain Thread#interrupt interrupted} while + * acquiring the read lock, * * </ul> * @@ -466,83 +747,83 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab } /** - * Acquires the shared lock only if it is not held exclusively by + * Acquires the read lock only if the write lock is not held by * another thread at the time of invocation. * - * <p>Acquires the lock if it is not held exclusively by + * <p>Acquires the read lock if the write lock is not held by * another thread and returns immediately with the value - * <tt>true</tt>. Even when this lock has been set to use a - * fair ordering policy, a call to <tt>tryLock()</tt> - * <em>will</em> immediately acquire the lock if it is + * {@code true}. Even when this lock has been set to use a + * fair ordering policy, a call to {@code tryLock()} + * <em>will</em> immediately acquire the read lock if it is * available, whether or not other threads are currently - * waiting for the lock. This "barging" behavior + * waiting for the read lock. This "barging" behavior * can be useful in certain circumstances, even though it * breaks fairness. If you want to honor the fairness setting * for this lock, then use {@link #tryLock(long, TimeUnit) * tryLock(0, TimeUnit.SECONDS) } which is almost equivalent * (it also detects interruption). * - * <p>If the lock is held exclusively by another thread then + * <p>If the write lock is held by another thread then * this method will return immediately with the value - * <tt>false</tt>. + * {@code false}. * - * @return <tt>true</tt> if the lock was acquired. + * @return {@code true} if the read lock was acquired */ public boolean tryLock() { - return sync.nonfairTryAcquireShared(1) >= 0; + return sync.tryReadLock(); } /** - * Acquires the shared lock if it is not held exclusively by + * Acquires the read lock if the write lock is not held by * another thread within the given waiting time and the - * current thread has not been {@link Thread#interrupt + * current thread has not been {@linkplain Thread#interrupt * interrupted}. * - * <p>Acquires the lock if it is not held exclusively by + * <p>Acquires the read lock if the write lock is not held by * another thread and returns immediately with the value - * <tt>true</tt>. If this lock has been set to use a fair + * {@code true}. If this lock has been set to use a fair * ordering policy then an available lock <em>will not</em> be * acquired if any other threads are waiting for the * lock. This is in contrast to the {@link #tryLock()} - * method. If you want a timed <tt>tryLock</tt> that does + * method. If you want a timed {@code tryLock} that does * permit barging on a fair lock then combine the timed and * un-timed forms together: * * <pre>if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... } * </pre> * - * <p>If the lock is held exclusively by another thread then the - * current thread becomes disabled for thread scheduling + * <p>If the write lock is held by another thread then the + * current thread becomes disabled for thread scheduling * purposes and lies dormant until one of three things happens: * * <ul> * - * <li>The lock is acquired by the current thread; or + * <li>The read lock is acquired by the current thread; or * - * <li>Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + * <li>Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or * - * <li>The specified waiting time elapses + * <li>The specified waiting time elapses. * * </ul> * - * <p>If the lock is acquired then the value <tt>true</tt> is + * <p>If the read lock is acquired then the value {@code true} is * returned. * * <p>If the current thread: * * <ul> * - * <li>has its interrupted status set on entry to this method; or + * <li>has its interrupted status set on entry to this method; or * - * <li>is {@link Thread#interrupt interrupted} while acquiring - * the lock, + * <li>is {@linkplain Thread#interrupt interrupted} while + * acquiring the read lock, * * </ul> then {@link InterruptedException} is thrown and the * current thread's interrupted status is cleared. * * <p>If the specified waiting time elapses then the value - * <tt>false</tt> is returned. If the time is less than or + * {@code false} is returned. If the time is less than or * equal to zero, the method will not wait at all. * * <p>In this implementation, as this method is an explicit @@ -550,13 +831,11 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * the interrupt over normal or reentrant acquisition of the * lock, and over reporting the elapse of the waiting time. * - * @param timeout the time to wait for the lock + * @param timeout the time to wait for the read lock * @param unit the time unit of the timeout argument - * - * @return <tt>true</tt> if the lock was acquired. - * + * @return {@code true} if the read lock was acquired * @throws InterruptedException if the current thread is interrupted - * @throws NullPointerException if unit is null + * @throws NullPointerException if the time unit is null * */ public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { @@ -564,20 +843,19 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab } /** - * Attempts to release this lock. + * Attempts to release this lock. * * <p> If the number of readers is now zero then the lock - * is made available for other lock attempts. + * is made available for write lock attempts. */ public void unlock() { sync.releaseShared(1); } /** - * Throws UnsupportedOperationException because ReadLocks - * do not support conditions. - * @return A new {@link Condition} instance for this <tt>Lock</tt> - * instance. + * Throws {@code UnsupportedOperationException} because + * {@code ReadLocks} do not support conditions. + * * @throws UnsupportedOperationException always */ public Condition newCondition() { @@ -586,17 +864,16 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab /** * Returns a string identifying this lock, as well as its lock state. - * The state, in brackets, includes the String - * "Read locks =" followed by the number of held - * read locks. - * @return a string identifying this lock, as well as its lock state. + * The state, in brackets, includes the String {@code "Read locks ="} + * followed by the number of held read locks. + * + * @return a string identifying this lock, as well as its lock state */ public String toString() { int r = sync.getReadLockCount(); - return super.toString() + + return super.toString() + "[Read locks = " + r + "]"; } - } /** @@ -605,42 +882,45 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab public static class WriteLock implements Lock, java.io.Serializable { private static final long serialVersionUID = -4992448646407690164L; private final Sync sync; - - /** - * Constructor for use by subclasses. + + /** + * Constructor for use by subclasses + * * @param lock the outer lock object - * @throws NullPointerException if lock null + * @throws NullPointerException if the lock is null */ protected WriteLock(ReentrantReadWriteLock lock) { sync = lock.sync; } /** - * Acquire the lock. + * Acquires the write lock. * - * <p>Acquires the lock if it is not held by another thread - * and returns immediately, setting the lock hold count to + * <p>Acquires the write lock if neither the read nor write lock + * are held by another thread + * and returns immediately, setting the write lock hold count to * one. * - * <p>If the current thread already holds the lock then the + * <p>If the current thread already holds the write lock then the * hold count is incremented by one and the method returns * immediately. * * <p>If the lock is held by another thread then the current * thread becomes disabled for thread scheduling purposes and - * lies dormant until the lock has been acquired, at which - * time the lock hold count is set to one. + * lies dormant until the write lock has been acquired, at which + * time the write lock hold count is set to one. */ public void lock() { - sync.wlock(); + sync.acquire(1); } /** - * Acquires the lock unless the current thread is {@link - * Thread#interrupt interrupted}. + * Acquires the write lock unless the current thread is + * {@linkplain Thread#interrupt interrupted}. * - * <p>Acquires the lock if it is not held by another thread - * and returns immediately, setting the lock hold count to + * <p>Acquires the write lock if neither the read nor write lock + * are held by another thread + * and returns immediately, setting the write lock hold count to * one. * * <p>If the current thread already holds this lock then the @@ -653,14 +933,14 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * * <ul> * - * <li>The lock is acquired by the current thread; or + * <li>The write lock is acquired by the current thread; or * - * <li>Some other thread {@link Thread#interrupt interrupts} + * <li>Some other thread {@linkplain Thread#interrupt interrupts} * the current thread. * * </ul> * - * <p>If the lock is acquired by the current thread then the + * <p>If the write lock is acquired by the current thread then the * lock hold count is set to one. * * <p>If the current thread: @@ -670,8 +950,8 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * <li>has its interrupted status set on entry to this method; * or * - * <li>is {@link Thread#interrupt interrupted} while acquiring - * the lock, + * <li>is {@linkplain Thread#interrupt interrupted} while + * acquiring the write lock, * * </ul> * @@ -690,16 +970,17 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab } /** - * Acquires the lock only if it is not held by another thread + * Acquires the write lock only if it is not held by another thread * at the time of invocation. * - * <p>Acquires the lock if it is not held by another thread - * and returns immediately with the value <tt>true</tt>, - * setting the lock hold count to one. Even when this lock has + * <p>Acquires the write lock if neither the read nor write lock + * are held by another thread + * and returns immediately with the value {@code true}, + * setting the write lock hold count to one. Even when this lock has * been set to use a fair ordering policy, a call to - * <tt>tryLock()</tt> <em>will</em> immediately acquire the + * {@code tryLock()} <em>will</em> immediately acquire the * lock if it is available, whether or not other threads are - * currently waiting for the lock. This "barging" + * currently waiting for the write lock. This "barging" * behavior can be useful in certain circumstances, even * though it breaks fairness. If you want to honor the * fairness setting for this lock, then use {@link @@ -708,31 +989,32 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * * <p> If the current thread already holds this lock then the * hold count is incremented by one and the method returns - * <tt>true</tt>. + * {@code true}. * * <p>If the lock is held by another thread then this method - * will return immediately with the value <tt>false</tt>. + * will return immediately with the value {@code false}. * - * @return <tt>true</tt> if the lock was free and was acquired by the - * current thread, or the lock was already held by the current thread; and - * <tt>false</tt> otherwise. + * @return {@code true} if the lock was free and was acquired + * by the current thread, or the write lock was already held + * by the current thread; and {@code false} otherwise. */ public boolean tryLock( ) { - return sync.nonfairTryAcquire(1); + return sync.tryWriteLock(); } /** - * Acquires the lock if it is not held by another thread + * Acquires the write lock if it is not held by another thread * within the given waiting time and the current thread has - * not been {@link Thread#interrupt interrupted}. + * not been {@linkplain Thread#interrupt interrupted}. * - * <p>Acquires the lock if it is not held by another thread - * and returns immediately with the value <tt>true</tt>, - * setting the lock hold count to one. If this lock has been + * <p>Acquires the write lock if neither the read nor write lock + * are held by another thread + * and returns immediately with the value {@code true}, + * setting the write lock hold count to one. If this lock has been * set to use a fair ordering policy then an available lock * <em>will not</em> be acquired if any other threads are - * waiting for the lock. This is in contrast to the {@link - * #tryLock()} method. If you want a timed <tt>tryLock</tt> + * waiting for the write lock. This is in contrast to the {@link + * #tryLock()} method. If you want a timed {@code tryLock} * that does permit barging on a fair lock then combine the * timed and un-timed forms together: * @@ -741,7 +1023,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * * <p>If the current thread already holds this lock then the * hold count is incremented by one and the method returns - * <tt>true</tt>. + * {@code true}. * * <p>If the lock is held by another thread then the current * thread becomes disabled for thread scheduling purposes and @@ -749,17 +1031,17 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * * <ul> * - * <li>The lock is acquired by the current thread; or + * <li>The write lock is acquired by the current thread; or * - * <li>Some other thread {@link Thread#interrupt interrupts} + * <li>Some other thread {@linkplain Thread#interrupt interrupts} * the current thread; or * * <li>The specified waiting time elapses * * </ul> * - * <p>If the lock is acquired then the value <tt>true</tt> is - * returned and the lock hold count is set to one. + * <p>If the write lock is acquired then the value {@code true} is + * returned and the write lock hold count is set to one. * * <p>If the current thread: * @@ -768,16 +1050,16 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * <li>has its interrupted status set on entry to this method; * or * - * <li>is {@link Thread#interrupt interrupted} while acquiring - * the lock, + * <li>is {@linkplain Thread#interrupt interrupted} while + * acquiring the write lock, * - * </ul> + * </ul> * * then {@link InterruptedException} is thrown and the current * thread's interrupted status is cleared. * * <p>If the specified waiting time elapses then the value - * <tt>false</tt> is returned. If the time is less than or + * {@code false} is returned. If the time is less than or * equal to zero, the method will not wait at all. * * <p>In this implementation, as this method is an explicit @@ -785,30 +1067,31 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * the interrupt over normal or reentrant acquisition of the * lock, and over reporting the elapse of the waiting time. * - * @param timeout the time to wait for the lock + * @param timeout the time to wait for the write lock * @param unit the time unit of the timeout argument * - * @return <tt>true</tt> if the lock was free and was acquired - * by the current thread, or the lock was already held by the - * current thread; and <tt>false</tt> if the waiting time + * @return {@code true} if the lock was free and was acquired + * by the current thread, or the write lock was already held by the + * current thread; and {@code false} if the waiting time * elapsed before the lock could be acquired. * * @throws InterruptedException if the current thread is interrupted - * @throws NullPointerException if unit is null + * @throws NullPointerException if the time unit is null * */ public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } - + /** - * Attempts to release this lock. + * Attempts to release this lock. * * <p>If the current thread is the holder of this lock then * the hold count is decremented. If the hold count is now * zero then the lock is released. If the current thread is * not the holder of this lock then {@link * IllegalMonitorStateException} is thrown. + * * @throws IllegalMonitorStateException if the current thread does not * hold this lock. */ @@ -818,7 +1101,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab /** * Returns a {@link Condition} instance for use with this - * {@link Lock} instance. + * {@link Lock} instance. * <p>The returned {@link Condition} instance supports the same * usages as do the {@link Object} monitor methods ({@link * Object#wait() wait}, {@link Object#notify notify}, and {@link @@ -834,74 +1117,80 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * affected. However it is essentially always an error to * invoke a condition waiting method when the current thread * has also acquired read locks, since other threads that - * could unblock it will not be able to access the write + * could unblock it will not be able to acquire the write * lock.) * - * <li>When the condition {@link Condition#await() waiting} + * <li>When the condition {@linkplain Condition#await() waiting} * methods are called the write lock is released and, before * they return, the write lock is reacquired and the lock hold * count restored to what it was when the method was called. * - * <li>If a thread is {@link Thread#interrupt interrupted} while + * <li>If a thread is {@linkplain Thread#interrupt interrupted} while * waiting then the wait will terminate, an {@link * InterruptedException} will be thrown, and the thread's * interrupted status will be cleared. * - * <li> Waiting threads are signalled in FIFO order + * <li> Waiting threads are signalled in FIFO order. * * <li>The ordering of lock reacquisition for threads returning * from waiting methods is the same as for threads initially * acquiring the lock, which is in the default case not specified, * but for <em>fair</em> locks favors those threads that have been * waiting the longest. - * + * * </ul> + * * @return the Condition object */ - public Condition newCondition() { + public Condition newCondition() { return sync.newCondition(); } /** * Returns a string identifying this lock, as well as its lock * state. The state, in brackets includes either the String - * "Unlocked" or the String "Locked by" - * followed by the {@link Thread#getName} of the owning thread. - * @return a string identifying this lock, as well as its lock state. + * {@code "Unlocked"} or the String {@code "Locked by"} + * followed by the {@linkplain Thread#getName name} of the owning thread. + * + * @return a string identifying this lock, as well as its lock state */ public String toString() { - Thread owner = sync.getOwner(); - return super.toString() + ((owner == null) ? + Thread o = sync.getOwner(); + return super.toString() + ((o == null) ? "[Unlocked]" : - "[Locked by thread " + owner.getName() + "]"); + "[Locked by thread " + o.getName() + "]"); } } - // Instrumentation and status /** - * Returns true if this lock has fairness set true. - * @return true if this lock has fairness set true. + * Returns {@code true} if this lock has fairness set true. + * + * @return {@code true} if this lock has fairness set true */ public final boolean isFair() { return sync instanceof FairSync; } /** - * Returns the thread that currently owns the exclusive lock, or - * <tt>null</tt> if not owned. Note that the owner may be - * momentarily <tt>null</tt> even if there are threads trying to - * acquire the lock but have not yet done so. This method is - * designed to facilitate construction of subclasses that provide - * more extensive lock monitoring facilities. - * @return the owner, or <tt>null</tt> if not owned. + * Returns the thread that currently owns the write lock, or + * {@code null} if not owned. When this method is called by a + * thread that is not the owner, the return value reflects a + * best-effort approximation of current lock status. For example, + * the owner may be momentarily {@code null} even if there are + * threads trying to acquire the lock but have not yet done so. + * This method is designed to facilitate construction of + * subclasses that provide more extensive lock monitoring + * facilities. + * + * @return the owner, or {@code null} if not owned */ protected Thread getOwner() { return sync.getOwner(); } - + /** * Queries the number of read locks held for this lock. This * method is designed for use in monitoring system state, not for @@ -916,17 +1205,19 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * Queries if the write lock is held by any thread. This method is * designed for use in monitoring system state, not for * synchronization control. - * @return <tt>true</tt> if any thread holds write lock and - * <tt>false</tt> otherwise. + * + * @return {@code true} if any thread holds the write lock and + * {@code false} otherwise */ public boolean isWriteLocked() { return sync.isWriteLocked(); } /** - * Queries if the write lock is held by the current thread. - * @return <tt>true</tt> if current thread holds this lock and - * <tt>false</tt> otherwise. + * Queries if the write lock is held by the current thread. + * + * @return {@code true} if the current thread holds the write lock and + * {@code false} otherwise */ public boolean isWriteLockedByCurrentThread() { return sync.isHeldExclusively(); @@ -937,8 +1228,8 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * current thread. A writer thread has a hold on a lock for * each lock action that is not matched by an unlock action. * - * @return the number of holds on this lock by the current thread, - * or zero if this lock is not held by the current thread. + * @return the number of holds on the write lock by the current thread, + * or zero if the write lock is not held by the current thread */ public int getWriteHoldCount() { return sync.getWriteHoldCount(); @@ -952,6 +1243,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * returned collection are in no particular order. This method is * designed to facilitate construction of subclasses that provide * more extensive lock monitoring facilities. + * * @return the collection of threads */ protected Collection<Thread> getQueuedWriterThreads() { @@ -966,6 +1258,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * returned collection are in no particular order. This method is * designed to facilitate construction of subclasses that provide * more extensive lock monitoring facilities. + * * @return the collection of threads */ protected Collection<Thread> getQueuedReaderThreads() { @@ -973,41 +1266,42 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab } /** - * Queries whether any threads are waiting to acquire. Note that - * because cancellations may occur at any time, a <tt>true</tt> - * return does not guarantee that any other thread will ever - * acquire. This method is designed primarily for use in - * monitoring of the system state. + * Queries whether any threads are waiting to acquire the read or + * write lock. Note that because cancellations may occur at any + * time, a {@code true} return does not guarantee that any other + * thread will ever acquire a lock. This method is designed + * primarily for use in monitoring of the system state. * - * @return true if there may be other threads waiting to acquire - * the lock. + * @return {@code true} if there may be other threads waiting to + * acquire the lock */ - public final boolean hasQueuedThreads() { + public final boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } /** - * Queries whether the given thread is waiting to acquire this - * lock. Note that because cancellations may occur at any time, a - * <tt>true</tt> return does not guarantee that this thread - * will ever acquire. This method is designed primarily for use - * in monitoring of the system state. + * Queries whether the given thread is waiting to acquire either + * the read or write lock. Note that because cancellations may + * occur at any time, a {@code true} return does not guarantee + * that this thread will ever acquire a lock. This method is + * designed primarily for use in monitoring of the system state. * * @param thread the thread - * @return true if the given thread is queued waiting for this lock. - * @throws NullPointerException if thread is null + * @return {@code true} if the given thread is queued waiting for this lock + * @throws NullPointerException if the thread is null */ - public final boolean hasQueuedThread(Thread thread) { + public final boolean hasQueuedThread(Thread thread) { return sync.isQueued(thread); } /** - * Returns an estimate of the number of threads waiting to - * acquire. The value is only an estimate because the number of - * threads may change dynamically while this method traverses - * internal data structures. This method is designed for use in - * monitoring of the system state, not for synchronization - * control. + * Returns an estimate of the number of threads waiting to acquire + * either the read or write lock. The value is only an estimate + * because the number of threads may change dynamically while this + * method traverses internal data structures. This method is + * designed for use in monitoring of the system state, not for + * synchronization control. + * * @return the estimated number of threads waiting for this lock */ public final int getQueueLength() { @@ -1016,12 +1310,13 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab /** * Returns a collection containing threads that may be waiting to - * acquire. Because the actual set of threads may change - * dynamically while constructing this result, the returned - * collection is only a best-effort estimate. The elements of the - * returned collection are in no particular order. This method is - * designed to facilitate construction of subclasses that provide - * more extensive monitoring facilities. + * acquire either the read or write lock. Because the actual set + * of threads may change dynamically while constructing this + * result, the returned collection is only a best-effort estimate. + * The elements of the returned collection are in no particular + * order. This method is designed to facilitate construction of + * subclasses that provide more extensive monitoring facilities. + * * @return the collection of threads */ protected Collection<Thread> getQueuedThreads() { @@ -1031,18 +1326,18 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab /** * Queries whether any threads are waiting on the given condition * associated with the write lock. Note that because timeouts and - * interrupts may occur at any time, a <tt>true</tt> return does - * not guarantee that a future <tt>signal</tt> will awaken any + * interrupts may occur at any time, a {@code true} return does + * not guarantee that a future {@code signal} will awaken any * threads. This method is designed primarily for use in * monitoring of the system state. + * * @param condition the condition - * @return <tt>true</tt> if there are any waiting threads. - * @throws IllegalMonitorStateException if this lock - * is not held + * @return {@code true} if there are any waiting threads + * @throws IllegalMonitorStateException if this lock is not held * @throws IllegalArgumentException if the given condition is - * not associated with this lock - * @throws NullPointerException if condition null - */ + * not associated with this lock + * @throws NullPointerException if the condition is null + */ public boolean hasWaiters(Condition condition) { if (condition == null) throw new NullPointerException(); @@ -1058,14 +1353,14 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * serves only as an upper bound on the actual number of waiters. * This method is designed for use in monitoring of the system * state, not for synchronization control. + * * @param condition the condition - * @return the estimated number of waiting threads. - * @throws IllegalMonitorStateException if this lock - * is not held + * @return the estimated number of waiting threads + * @throws IllegalMonitorStateException if this lock is not held * @throws IllegalArgumentException if the given condition is - * not associated with this lock - * @throws NullPointerException if condition null - */ + * not associated with this lock + * @throws NullPointerException if the condition is null + */ public int getWaitQueueLength(Condition condition) { if (condition == null) throw new NullPointerException(); @@ -1083,13 +1378,13 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * are in no particular order. This method is designed to * facilitate construction of subclasses that provide more * extensive condition monitoring facilities. + * * @param condition the condition * @return the collection of threads - * @throws IllegalMonitorStateException if this lock - * is not held + * @throws IllegalMonitorStateException if this lock is not held * @throws IllegalArgumentException if the given condition is - * not associated with this lock - * @throws NullPointerException if condition null + * not associated with this lock + * @throws NullPointerException if the condition is null */ protected Collection<Thread> getWaitingThreads(Condition condition) { if (condition == null) @@ -1101,18 +1396,19 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab /** * Returns a string identifying this lock, as well as its lock state. - * The state, in brackets, includes the String "Write locks =" - * follwed by the number of reentrantly held write locks, and the - * String "Read locks =" followed by the number of held + * The state, in brackets, includes the String {@code "Write locks ="} + * followed by the number of reentrantly held write locks, and the + * String {@code "Read locks ="} followed by the number of held * read locks. - * @return a string identifying this lock, as well as its lock state. + * + * @return a string identifying this lock, as well as its lock state */ public String toString() { int c = sync.getCount(); - int w = exclusiveCount(c); - int r = sharedCount(c); - - return super.toString() + + int w = Sync.exclusiveCount(c); + int r = Sync.sharedCount(c); + + return super.toString() + "[Write locks = " + w + ", Read locks = " + r + "]"; } diff --git a/concurrent/src/main/java/java/util/concurrent/locks/package-info.java b/concurrent/src/main/java/java/util/concurrent/locks/package-info.java new file mode 100644 index 0000000..12560af --- /dev/null +++ b/concurrent/src/main/java/java/util/concurrent/locks/package-info.java @@ -0,0 +1,47 @@ +/* + * 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 + */ + +/** + * Interfaces and classes providing a framework for locking and waiting + * for conditions that is distinct from built-in synchronization and + * monitors. The framework permits much greater flexibility in the use of + * locks and conditions, at the expense of more awkward syntax. + * + * <p>The {@link java.util.concurrent.locks.Lock} interface supports + * locking disciplines that differ in semantics (reentrant, fair, etc), + * and that can be used in non-block-structured contexts including + * hand-over-hand and lock reordering algorithms. The main implementation + * is {@link java.util.concurrent.locks.ReentrantLock}. + * + * <p>The {@link java.util.concurrent.locks.ReadWriteLock} interface + * similarly defines locks that may be shared among readers but are + * exclusive to writers. Only a single implementation, {@link + * java.util.concurrent.locks.ReentrantReadWriteLock}, is provided, since + * it covers most standard usage contexts. But programmers may create + * their own implementations to cover nonstandard requirements. + * + * <p>The {@link java.util.concurrent.locks.Condition} interface + * describes condition variables that may be associated with Locks. + * These are similar in usage to the implicit monitors accessed using + * {@code Object.wait}, but offer extended capabilities. + * In particular, multiple {@code Condition} objects may be associated + * with a single {@code Lock}. To avoid compatibility issues, the + * names of {@code Condition} methods are different from the + * corresponding {@code Object} versions. + * + * <p>The {@link java.util.concurrent.locks.AbstractQueuedSynchronizer} + * class serves as a useful superclass for defining locks and other + * synchronizers that rely on queuing blocked threads. The {@link + * java.util.concurrent.locks.AbstractQueuedLongSynchronizer} class + * provides the same functionality but extends support to 64 bits of + * synchronization state. The {@link java.util.concurrent.locks.LockSupport} + * class provides lower-level blocking and unblocking support that is + * useful for those developers implementing their own customized lock + * classes. + * + * @since 1.5 + */ +package java.util.concurrent.locks; diff --git a/concurrent/src/main/java/java/util/concurrent/locks/package.html b/concurrent/src/main/java/java/util/concurrent/locks/package.html deleted file mode 100644 index 1cc156f..0000000 --- a/concurrent/src/main/java/java/util/concurrent/locks/package.html +++ /dev/null @@ -1,44 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> -<html> <head> -<title>Locks</title> -</head> - -<body> - -Interfaces and classes providing a framework for locking and waiting -for conditions that is distinct from built-in synchronization and -monitors. The framework permits much greater flexibility in the use of -locks and conditions, at the expense of more awkward syntax. - -<p> The {@link java.util.concurrent.locks.Lock} interface supports -locking disciplines that differ in semantics (reentrant, fair, etc), -and that can be used in non-block-structured contexts including -hand-over-hand and lock reordering algorithms. The main implementation -is {@link java.util.concurrent.locks.ReentrantLock}. - -<p> The {@link java.util.concurrent.locks.ReadWriteLock} interface -similarly defines locks that may be shared among readers but are -exclusive to writers. Only a single implementation, {@link -java.util.concurrent.locks.ReentrantReadWriteLock}, is provided, since -it covers most standard usage contexts. But programmers may create -their own implementations to cover nonstandard requirements. - -<p> The {@link java.util.concurrent.locks.Condition} interface -describes condition variables that may be associated with Locks. -These are similar in usage to the implicit monitors accessed using -<tt>Object.wait</tt>, but offer extended capabilities. In particular, -multiple <tt>Condition</tt> objects may be associated with a single -<tt>Lock</tt>. To avoid compatibility issues, the names of -<tt>Condition</tt> methods are different than the corresponding -<tt>Object</tt> versions. - -<p> The {@link java.util.concurrent.locks.AbstractQueuedSynchronizer} -class serves as a useful superclass for defining locks and other -synchronizers that rely on queuing blocked threads. The {@link -java.util.concurrent.locks.LockSupport} class provides lower-level -blocking and unblocking support that is useful for those developers -implementing their own customized lock classes. - -@since Android 1.0 - -</body> </html> diff --git a/concurrent/src/main/java/java/util/concurrent/package-info.java b/concurrent/src/main/java/java/util/concurrent/package-info.java new file mode 100644 index 0000000..f77e16a --- /dev/null +++ b/concurrent/src/main/java/java/util/concurrent/package-info.java @@ -0,0 +1,233 @@ +/* + * 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 + */ + +/** + * Utility classes commonly useful in concurrent programming. This + * package includes a few small standardized extensible frameworks, as + * well as some classes that provide useful functionality and are + * otherwise tedious or difficult to implement. Here are brief + * descriptions of the main components. See also the + * {@link java.util.concurrent.locks} and + * {@link java.util.concurrent.atomic} packages. + * + * <h2>Executors</h2> + * + * <b>Interfaces.</b> + * + * {@link java.util.concurrent.Executor} is a simple standardized + * interface for defining custom thread-like subsystems, including + * thread pools, asynchronous IO, and lightweight task frameworks. + * Depending on which concrete Executor class is being used, tasks may + * execute in a newly created thread, an existing task-execution thread, + * or the thread calling {@link java.util.concurrent.Executor#execute + * execute}, and may execute sequentially or concurrently. + * + * {@link java.util.concurrent.ExecutorService} provides a more + * complete asynchronous task execution framework. An + * ExecutorService manages queuing and scheduling of tasks, + * and allows controlled shutdown. + * + * The {@link java.util.concurrent.ScheduledExecutorService} + * subinterface and associated interfaces add support for + * delayed and periodic task execution. ExecutorServices + * provide methods arranging asynchronous execution of any + * function expressed as {@link java.util.concurrent.Callable}, + * the result-bearing analog of {@link java.lang.Runnable}. + * + * A {@link java.util.concurrent.Future} returns the results of + * a function, allows determination of whether execution has + * completed, and provides a means to cancel execution. + * + * A {@link java.util.concurrent.RunnableFuture} is a {@code Future} + * that possesses a {@code run} method that upon execution, + * sets its results. + * + * <p> + * + * <b>Implementations.</b> + * + * Classes {@link java.util.concurrent.ThreadPoolExecutor} and + * {@link java.util.concurrent.ScheduledThreadPoolExecutor} + * provide tunable, flexible thread pools. + * + * The {@link java.util.concurrent.Executors} class provides + * factory methods for the most common kinds and configurations + * of Executors, as well as a few utility methods for using + * them. Other utilities based on {@code Executors} include the + * concrete class {@link java.util.concurrent.FutureTask} + * providing a common extensible implementation of Futures, and + * {@link java.util.concurrent.ExecutorCompletionService}, that + * assists in coordinating the processing of groups of + * asynchronous tasks. + * + * <h2>Queues</h2> + * + * The {@link java.util.concurrent.ConcurrentLinkedQueue} class + * supplies an efficient scalable thread-safe non-blocking FIFO + * queue. + * + * <p>Five implementations in {@code java.util.concurrent} support + * the extended {@link java.util.concurrent.BlockingQueue} + * interface, that defines blocking versions of put and take: + * {@link java.util.concurrent.LinkedBlockingQueue}, + * {@link java.util.concurrent.ArrayBlockingQueue}, + * {@link java.util.concurrent.SynchronousQueue}, + * {@link java.util.concurrent.PriorityBlockingQueue}, and + * {@link java.util.concurrent.DelayQueue}. + * The different classes cover the most common usage contexts + * for producer-consumer, messaging, parallel tasking, and + * related concurrent designs. + * + * <h2>Timing</h2> + * + * The {@link java.util.concurrent.TimeUnit} class provides + * multiple granularities (including nanoseconds) for + * specifying and controlling time-out based operations. Most + * classes in the package contain operations based on time-outs + * in addition to indefinite waits. In all cases that + * time-outs are used, the time-out specifies the minimum time + * that the method should wait before indicating that it + * timed-out. Implementations make a "best effort" + * to detect time-outs as soon as possible after they occur. + * However, an indefinite amount of time may elapse between a + * time-out being detected and a thread actually executing + * again after that time-out. All methods that accept timeout + * parameters treat values less than or equal to zero to mean + * not to wait at all. To wait "forever", you can use a value + * of {@code Long.MAX_VALUE}. + * + * <h2>Synchronizers</h2> + * + * Four classes aid common special-purpose synchronization idioms. + * {@link java.util.concurrent.Semaphore} is a classic concurrency tool. + * {@link java.util.concurrent.CountDownLatch} is a very simple yet very + * common utility for blocking until a given number of signals, events, + * or conditions hold. A {@link java.util.concurrent.CyclicBarrier} is a + * resettable multiway synchronization point useful in some styles of + * parallel programming. An {@link java.util.concurrent.Exchanger} allows + * two threads to exchange objects at a rendezvous point, and is useful + * in several pipeline designs. + * + * <h2>Concurrent Collections</h2> + * + * Besides Queues, this package supplies Collection implementations + * designed for use in multithreaded contexts: + * {@link java.util.concurrent.ConcurrentHashMap}, + * {@link java.util.concurrent.CopyOnWriteArrayList}, and + * {@link java.util.concurrent.CopyOnWriteArraySet}. + * When many threads are expected to access a given collection, a + * {@code ConcurrentHashMap} is normally preferable to a synchronized + * {@code HashMap}. A {@code CopyOnWriteArrayList} is preferable to a + * synchronized {@code ArrayList} when the expected number of reads and + * traversals greatly outnumber the number of updates to a list. + + * <p>The "Concurrent" prefix used with some classes in this package + * is a shorthand indicating several differences from similar + * "synchronized" classes. For example {@code java.util.Hashtable} and + * {@code Collections.synchronizedMap(new HashMap())} are + * synchronized. But {@link + * java.util.concurrent.ConcurrentHashMap} is "concurrent". A + * concurrent collection is thread-safe, but not governed by a + * single exclusion lock. In the particular case of + * ConcurrentHashMap, it safely permits any number of + * concurrent reads as well as a tunable number of concurrent + * writes. "Synchronized" classes can be useful when you need + * to prevent all access to a collection via a single lock, at + * the expense of poorer scalability. In other cases in which + * multiple threads are expected to access a common collection, + * "concurrent" versions are normally preferable. And + * unsynchronized collections are preferable when either + * collections are unshared, or are accessible only when + * holding other locks. + * + * <p>Most concurrent Collection implementations (including most + * Queues) also differ from the usual java.util conventions in that + * their Iterators provide <em>weakly consistent</em> rather than + * fast-fail traversal. A weakly consistent iterator is thread-safe, + * but does not necessarily freeze the collection while iterating, so + * it may (or may not) reflect any updates since the iterator was + * created. + * + * <h2><a name="MemoryVisibility">Memory Consistency Properties</a></h2> + * + * <a href="http://java.sun.com/docs/books/jls/third_edition/html/memory.html"> + * Chapter 17 of the Java Language Specification</a> defines the + * <i>happens-before</i> relation on memory operations such as reads and + * writes of shared variables. The results of a write by one thread are + * guaranteed to be visible to a read by another thread only if the write + * operation <i>happens-before</i> the read operation. The + * {@code synchronized} and {@code volatile} constructs, as well as the + * {@code Thread.start()} and {@code Thread.join()} methods, can form + * <i>happens-before</i> relationships. In particular: + * + * <ul> + * <li>Each action in a thread <i>happens-before</i> every action in that + * thread that comes later in the program's order. + * + * <li>An unlock ({@code synchronized} block or method exit) of a + * monitor <i>happens-before</i> every subsequent lock ({@code synchronized} + * block or method entry) of that same monitor. And because + * the <i>happens-before</i> relation is transitive, all actions + * of a thread prior to unlocking <i>happen-before</i> all actions + * subsequent to any thread locking that monitor. + * + * <li>A write to a {@code volatile} field <i>happens-before</i> every + * subsequent read of that same field. Writes and reads of + * {@code volatile} fields have similar memory consistency effects + * as entering and exiting monitors, but do <em>not</em> entail + * mutual exclusion locking. + * + * <li>A call to {@code start} on a thread <i>happens-before</i> any + * action in the started thread. + * + * <li>All actions in a thread <i>happen-before</i> any other thread + * successfully returns from a {@code join} on that thread. + * + * </ul> + * + * + * The methods of all classes in {@code java.util.concurrent} and its + * subpackages extend these guarantees to higher-level + * synchronization. In particular: + * + * <ul> + * + * <li>Actions in a thread prior to placing an object into any concurrent + * collection <i>happen-before</i> actions subsequent to the access or + * removal of that element from the collection in another thread. + * + * <li>Actions in a thread prior to the submission of a {@code Runnable} + * to an {@code Executor} <i>happen-before</i> its execution begins. + * Similarly for {@code Callables} submitted to an {@code ExecutorService}. + * + * <li>Actions taken by the asynchronous computation represented by a + * {@code Future} <i>happen-before</i> actions subsequent to the + * retrieval of the result via {@code Future.get()} in another thread. + * + * <li>Actions prior to "releasing" synchronizer methods such as + * {@code Lock.unlock}, {@code Semaphore.release}, and + * {@code CountDownLatch.countDown} <i>happen-before</i> actions + * subsequent to a successful "acquiring" method such as + * {@code Lock.lock}, {@code Semaphore.acquire}, + * {@code Condition.await}, and {@code CountDownLatch.await} on the + * same synchronizer object in another thread. + * + * <li>For each pair of threads that successfully exchange objects via + * an {@code Exchanger}, actions prior to the {@code exchange()} + * in each thread <i>happen-before</i> those subsequent to the + * corresponding {@code exchange()} in another thread. + * + * <li>Actions prior to calling {@code CyclicBarrier.await} + * <i>happen-before</i> actions performed by the barrier action, and + * actions performed by the barrier action <i>happen-before</i> actions + * subsequent to a successful return from the corresponding {@code await} + * in other threads. + * + * </ul> + * + * @since 1.5 + */ +package java.util.concurrent; diff --git a/concurrent/src/main/java/java/util/concurrent/package.html b/concurrent/src/main/java/java/util/concurrent/package.html deleted file mode 100644 index a5bb8d2..0000000 --- a/concurrent/src/main/java/java/util/concurrent/package.html +++ /dev/null @@ -1,125 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> -<html> <head> -<title>Concurrency Utilities</title> -</head> - -<body> - -<p> Utility classes commonly useful in concurrent programming. This -package includes a few small standardized extensible frameworks, as -well as some classes that provide useful functionality and are -otherwise tedious or difficult to implement. Here are brief -descriptions of the main components. See also the <tt>locks</tt> and -<tt>atomic</tt> packages. - -<h2>Executors</h2> - -<b>Interfaces.</b> {@link java.util.concurrent.Executor} is a simple -standardized interface for defining custom thread-like subsystems, -including thread pools, asynchronous IO, and lightweight task -frameworks. Depending on which concrete Executor class is being used, -tasks may execute in a newly created thread, an existing -task-execution thread, or the thread calling <tt>execute()</tt>, and -may execute sequentially or concurrently. {@link -java.util.concurrent.ExecutorService} provides a more complete -asynchronous task execution framework. An ExecutorService manages -queuing and scheduling of tasks, and allows controlled shutdown. The -{@link java.util.concurrent.ScheduledExecutorService} subinterface -adds support for delayed and periodic task execution. -ExecutorServices provide methods arranging asynchronous execution of -any function expressed as {@link java.util.concurrent.Callable}, the -result-bearing analog of {@link java.lang.Runnable}. A {@link -java.util.concurrent.Future} returns the results of a function, allows -determination of whether execution has completed, and provides a means to -cancel execution. - -<p> - -<b>Implementations.</b> Classes {@link -java.util.concurrent.ThreadPoolExecutor} and {@link -java.util.concurrent.ScheduledThreadPoolExecutor} provide tunable, -flexible thread pools. The {@link java.util.concurrent.Executors} -class provides factory methods for the most common kinds and -configurations of Executors, as well as a few utility methods for -using them. Other utilities based on Executors include the concrete -class {@link java.util.concurrent.FutureTask} providing a common -extensible implementation of Futures, and {@link -java.util.concurrent.ExecutorCompletionService}, that assists in -coordinating the processing of groups of asynchronous tasks. - -<h2>Queues</h2> - -The java.util.concurrent {@link -java.util.concurrent.ConcurrentLinkedQueue} class supplies an -efficient scalable thread-safe non-blocking FIFO queue. Five -implementations in java.util.concurrent support the extended {@link -java.util.concurrent.BlockingQueue} interface, that defines blocking -versions of put and take: {@link -java.util.concurrent.LinkedBlockingQueue}, {@link -java.util.concurrent.ArrayBlockingQueue}, {@link -java.util.concurrent.SynchronousQueue}, {@link -java.util.concurrent.PriorityBlockingQueue}, and {@link -java.util.concurrent.DelayQueue}. The different classes cover the most -common usage contexts for producer-consumer, messaging, parallel -tasking, and related concurrent designs. - - -<h2>Timing</h2> - -The {@link java.util.concurrent.TimeUnit} class provides multiple -granularities (including nanoseconds) for specifying and controlling -time-out based operations. Most classes in the package contain -operations based on time-outs in addition to indefinite waits. In all -cases that time-outs are used, the time-out specifies the minimum time -that the method should wait before indicating that it -timed-out. Implementations make a "best effort" to detect -time-outs as soon as possible after they occur. However, an indefinite -amount of time may elapse between a time-out being detected and a -thread actually executing again after that time-out. - -<h2>Synchronizers</h2> - -Four classes aid common special-purpose synchronization idioms. -{@link java.util.concurrent.Semaphore} is a classic concurrency tool. -{@link java.util.concurrent.CountDownLatch} is a very simple yet very -common utility for blocking until a given number of signals, events, -or conditions hold. A {@link java.util.concurrent.CyclicBarrier} is a -resettable multiway synchronization point useful in some styles of -parallel programming. An {@link java.util.concurrent.Exchanger} allows -two threads to exchange objects at a rendezvous point, and is useful -in several pipeline designs. - -<h2>Concurrent Collections</h2> - -Besides Queues, this package supplies a few Collection implementations -designed for use in multithreaded contexts: {@link -java.util.concurrent.ConcurrentHashMap}, {@link -java.util.concurrent.CopyOnWriteArrayList}, and {@link -java.util.concurrent.CopyOnWriteArraySet}. - -<p>The "Concurrent" prefix used with some classes in this package is a -shorthand indicating several differences from similar "synchronized" -classes. For example <tt>java.util.Hashtable</tt> and -<tt>Collections.synchronizedMap(new HashMap())</tt> are -synchronized. But {@link java.util.concurrent.ConcurrentHashMap} is -"concurrent". A concurrent collection is thread-safe, but not -governed by a single exclusion lock. In the particular case of -ConcurrentHashMap, it safely permits any number of concurrent reads as -well as a tunable number of concurrent writes. "Synchronized" classes -can be useful when you need to prevent all access to a collection via -a single lock, at the expense of poorer scalability. In other cases in -which multiple threads are expected to access a common collection, -"concurrent" versions are normally preferable. And unsynchronized -collections are preferable when either collections are unshared, or -are accessible only when holding other locks. - -<p> Most concurrent Collection implementations (including most Queues) -also differ from the usual java.util conventions in that their Iterators -provide <em>weakly consistent</em> rather than fast-fail traversal. A -weakly consistent iterator is thread-safe, but does not necessarily -freeze the collection while iterating, so it may (or may not) reflect -any updates since the iterator was created. - -@since Android 1.0 - -</body> </html> diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/AbstractQueuedSynchronizerTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/AbstractQueuedSynchronizerTest.java index 7102c14..db89645 100644 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/AbstractQueuedSynchronizerTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/AbstractQueuedSynchronizerTest.java @@ -462,7 +462,9 @@ public class AbstractQueuedSynchronizerTest extends JSR166TestCase { Thread t = new Thread(new InterruptedSyncRunnable(sync)); try { t.start(); + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); + Thread.sleep(SHORT_DELAY_MS); sync.release(1); t.join(); } catch(Exception e){ @@ -951,7 +953,6 @@ public class AbstractQueuedSynchronizerTest extends JSR166TestCase { sync.acquire(1); c.signal(); sync.release(1); - assert(t.isInterrupted()); t.join(SHORT_DELAY_MS); assertFalse(t.isAlive()); } diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/ArrayBlockingQueueTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/ArrayBlockingQueueTest.java index da6988f..b650ede 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/ArrayBlockingQueueTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/ArrayBlockingQueueTest.java @@ -651,6 +651,7 @@ public class ArrayBlockingQueueTest extends JSR166TestCase { assertEquals(SIZE, q.remainingCapacity()); q.add(one); assertFalse(q.isEmpty()); + assertTrue(q.contains(one)); q.clear(); assertTrue(q.isEmpty()); } @@ -971,6 +972,17 @@ public class ArrayBlockingQueueTest extends JSR166TestCase { assertEquals(l.size(), SIZE); for (int i = 0; i < SIZE; ++i) assertEquals(l.get(i), new Integer(i)); + q.add(zero); + q.add(one); + assertFalse(q.isEmpty()); + assertTrue(q.contains(zero)); + assertTrue(q.contains(one)); + l.clear(); + q.drainTo(l); + assertEquals(q.size(), 0); + assertEquals(l.size(), 2); + for (int i = 0; i < 2; ++i) + assertEquals(l.get(i), new Integer(i)); } /** @@ -1029,15 +1041,18 @@ public class ArrayBlockingQueueTest extends JSR166TestCase { * drainTo(c, n) empties first max {n, size} elements of queue into c */ public void testDrainToN() { + ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE*2); for (int i = 0; i < SIZE + 2; ++i) { - ArrayBlockingQueue q = populatedQueue(SIZE); + for(int j = 0; j < SIZE; j++) + assertTrue(q.offer(new Integer(j))); ArrayList l = new ArrayList(); q.drainTo(l, i); int k = (i < SIZE)? i : SIZE; - assertEquals(q.size(), SIZE-k); assertEquals(l.size(), k); + assertEquals(q.size(), SIZE-k); for (int j = 0; j < k; ++j) assertEquals(l.get(j), new Integer(j)); + while (q.poll() != null) ; } } diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerFieldUpdaterTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerFieldUpdaterTest.java index 03fd2c8..4c3ecb4 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerFieldUpdaterTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerFieldUpdaterTest.java @@ -62,6 +62,27 @@ public class AtomicIntegerFieldUpdaterTest extends JSR166TestCase { catch (RuntimeException rt) {} } + static class Base { + protected volatile int f = 0; + } + static class Sub1 extends Base { + AtomicIntegerFieldUpdater<Base> fUpdater + = AtomicIntegerFieldUpdater.newUpdater(Base.class, "f"); + } + static class Sub2 extends Base {} + + public void testProtectedFieldOnAnotherSubtype() { + Sub1 sub1 = new Sub1(); + Sub2 sub2 = new Sub2(); + + sub1.fUpdater.set(sub1, 1); + try { + sub1.fUpdater.set(sub2, 2); + shouldThrow(); + } + catch (RuntimeException rt) {} + } + /** * get returns the last value set or assigned */ @@ -80,6 +101,7 @@ public class AtomicIntegerFieldUpdaterTest extends JSR166TestCase { assertEquals(-3,a.get(this)); } + /** * compareAndSet succeeds in changing value if equal to expected else fails */ diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerTest.java index 1771d80..d484785 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerTest.java @@ -48,6 +48,7 @@ public class AtomicIntegerTest extends JSR166TestCase { assertEquals(-3,ai.get()); } + /** * compareAndSet succeeds in changing value if equal to expected else fails */ diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongFieldUpdaterTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongFieldUpdaterTest.java index 8076f7b..9310795 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongFieldUpdaterTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongFieldUpdaterTest.java @@ -29,7 +29,7 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase { */ public void testConstructor(){ try{ - AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> + AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a = AtomicLongFieldUpdater.newUpdater (AtomicLongFieldUpdaterTest.class, "y"); shouldThrow(); @@ -42,7 +42,7 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase { */ public void testConstructor2(){ try{ - AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> + AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a = AtomicLongFieldUpdater.newUpdater (AtomicLongFieldUpdaterTest.class, "z"); shouldThrow(); @@ -55,7 +55,7 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase { */ public void testConstructor3(){ try{ - AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> + AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a = AtomicLongFieldUpdater.newUpdater (AtomicLongFieldUpdaterTest.class, "w"); shouldThrow(); @@ -64,6 +64,27 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase { catch (RuntimeException rt) {} } + static class Base { + protected volatile long f = 0; + } + static class Sub1 extends Base { + AtomicLongFieldUpdater<Base> fUpdater + = AtomicLongFieldUpdater.newUpdater(Base.class, "f"); + } + static class Sub2 extends Base {} + + public void testProtectedFieldOnAnotherSubtype() { + Sub1 sub1 = new Sub1(); + Sub2 sub2 = new Sub2(); + + sub1.fUpdater.set(sub1, 1); + try { + sub1.fUpdater.set(sub2, 2); + shouldThrow(); + } + catch (RuntimeException rt) {} + } + /** * get returns the last value set or assigned */ @@ -75,12 +96,12 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase { return; } x = 1; - assertEquals(1,a.get(this)); - a.set(this,2); - assertEquals(2,a.get(this)); - a.set(this,-3); - assertEquals(-3,a.get(this)); - + assertEquals(1,a.get(this)); + a.set(this,2); + assertEquals(2,a.get(this)); + a.set(this,-3); + assertEquals(-3,a.get(this)); + } /** * compareAndSet succeeds in changing value if equal to expected else fails @@ -134,7 +155,7 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase { /** * repeated weakCompareAndSet succeeds in changing value when equal - * to expected + * to expected */ public void testWeakCompareAndSet(){ AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a; diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongTest.java index 55b8a49..143c84a 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongTest.java @@ -48,6 +48,7 @@ public class AtomicLongTest extends JSR166TestCase { assertEquals(-3,ai.get()); } + /** * compareAndSet succeeds in changing value if equal to expected else fails */ diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceFieldUpdaterTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceFieldUpdaterTest.java index 183ca21..feddce7 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceFieldUpdaterTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceFieldUpdaterTest.java @@ -64,6 +64,27 @@ public class AtomicReferenceFieldUpdaterTest extends JSR166TestCase{ catch (RuntimeException rt) {} } + static class Base { + protected volatile Object f = null; + } + static class Sub1 extends Base { + AtomicReferenceFieldUpdater<Base, Object> fUpdater + = AtomicReferenceFieldUpdater.newUpdater(Base.class, Object.class, "f"); + } + static class Sub2 extends Base {} + + public void testProtectedFieldOnAnotherSubtype() { + Sub1 sub1 = new Sub1(); + Sub2 sub2 = new Sub2(); + + sub1.fUpdater.set(sub1, "f"); + try { + sub1.fUpdater.set(sub2, "g"); + shouldThrow(); + } + catch (RuntimeException rt) {} + } + /** * get returns the last value set or assigned */ @@ -75,14 +96,11 @@ public class AtomicReferenceFieldUpdaterTest extends JSR166TestCase{ return; } x = one; - assertEquals(one,a.get(this)); - a.set(this,two); - assertEquals(two,a.get(this)); - a.set(this,-3); - // BEGIN android-changed - assertEquals(new Integer(-3),a.get(this)); - // END android-changed - + assertEquals(one,a.get(this)); + a.set(this,two); + assertEquals(two,a.get(this)); + a.set(this,m3); + assertEquals(m3,a.get(this)); } /** * compareAndSet succeeds in changing value if equal to expected else fails @@ -135,7 +153,7 @@ public class AtomicReferenceFieldUpdaterTest extends JSR166TestCase{ /** * repeated weakCompareAndSet succeeds in changing value when equal - * to expected + * to expected */ public void testWeakCompareAndSet(){ AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer>a; diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceTest.java index 0aa48cd..33da30d 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceTest.java @@ -46,7 +46,7 @@ public class AtomicReferenceTest extends JSR166TestCase { assertEquals(two,ai.get()); ai.set(m3); assertEquals(m3,ai.get()); - + } /** * compareAndSet succeeds in changing value if equal to expected else fails @@ -86,7 +86,7 @@ public class AtomicReferenceTest extends JSR166TestCase { /** * repeated weakCompareAndSet succeeds in changing value when equal - * to expected + * to expected */ public void testWeakCompareAndSet(){ AtomicReference ai = new AtomicReference(one); diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/ConcurrentHashMapTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/ConcurrentHashMapTest.java index 9b3ec52..d7f2210 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/ConcurrentHashMapTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/ConcurrentHashMapTest.java @@ -16,59 +16,16 @@ import java.io.*; public class ConcurrentHashMapTest extends JSR166TestCase{ public static void main(String[] args) { - junit.textui.TestRunner.run (suite()); + junit.textui.TestRunner.run (suite()); } public static Test suite() { return new TestSuite(ConcurrentHashMapTest.class); } - // BEGIN android-added - static class MyConcurrentHashMap<V, K> extends ConcurrentHashMap<K, V> - implements Cloneable { - - public MyConcurrentHashMap() { - super(); - } - - public MyConcurrentHashMap(int initialCapacity, float loadFactor, - int concurrencyLevel) { - super(initialCapacity, loadFactor, concurrencyLevel); - } - - public MyConcurrentHashMap(int initialCapacity) { - super(initialCapacity); - } - - public MyConcurrentHashMap(Map<? extends K, ? extends V> t) { - super(t); - } - - @Override - protected Object clone() throws CloneNotSupportedException { - return super.clone(); - } - - } /** * Create a map from Integers 1-5 to Strings "A"-"E". */ - private static MyConcurrentHashMap myMap5() { - MyConcurrentHashMap map = new MyConcurrentHashMap(5); - assertTrue(map.isEmpty()); - map.put(one, "A"); - map.put(two, "B"); - map.put(three, "C"); - map.put(four, "D"); - map.put(five, "E"); - assertFalse(map.isEmpty()); - assertEquals(5, map.size()); - return map; - } - // END android-added - /** - * Create a map from Integers 1-5 to Strings "A"-"E". - */ - private static ConcurrentHashMap map5() { + private static ConcurrentHashMap map5() { ConcurrentHashMap map = new ConcurrentHashMap(5); assertTrue(map.isEmpty()); map.put(one, "A"); @@ -111,7 +68,7 @@ public class ConcurrentHashMapTest extends JSR166TestCase{ assertTrue(map.contains("A")); assertFalse(map.contains("Z")); } - + /** * containsKey returns true for contained key */ @@ -126,8 +83,8 @@ public class ConcurrentHashMapTest extends JSR166TestCase{ */ public void testContainsValue() { ConcurrentHashMap map = map5(); - assertTrue(map.contains("A")); - assertFalse(map.contains("Z")); + assertTrue(map.containsValue("A")); + assertFalse(map.containsValue("Z")); } /** @@ -146,21 +103,6 @@ public class ConcurrentHashMapTest extends JSR166TestCase{ } /** - * Clone creates an equal map - */ - public void testClone() { - // BEGIN android-changed - MyConcurrentHashMap map =myMap5(); - try { - MyConcurrentHashMap m2 = (MyConcurrentHashMap)(map.clone()); - assertEquals(map, m2); - } catch (CloneNotSupportedException e) { - fail("clone not supported"); - } - // END android-changed - } - - /** * get returns the correct element at the given key, * or null if not present */ @@ -210,6 +152,49 @@ public class ConcurrentHashMapTest extends JSR166TestCase{ } /** + * keySet.toArray returns contains all keys + */ + public void testKeySetToArray() { + ConcurrentHashMap map = map5(); + Set s = map.keySet(); + Object[] ar = s.toArray(); + assertTrue(s.containsAll(Arrays.asList(ar))); + assertEquals(5, ar.length); + ar[0] = m10; + assertFalse(s.containsAll(Arrays.asList(ar))); + } + + /** + * Values.toArray contains all values + */ + public void testValuesToArray() { + ConcurrentHashMap map = map5(); + Collection v = map.values(); + Object[] ar = v.toArray(); + ArrayList s = new ArrayList(Arrays.asList(ar)); + assertEquals(5, ar.length); + assertTrue(s.contains("A")); + assertTrue(s.contains("B")); + assertTrue(s.contains("C")); + assertTrue(s.contains("D")); + assertTrue(s.contains("E")); + } + + /** + * entrySet.toArray contains all entries + */ + public void testEntrySetToArray() { + ConcurrentHashMap map = map5(); + Set s = map.entrySet(); + Object[] ar = s.toArray(); + assertEquals(5, ar.length); + for (int i = 0; i < 5; ++i) { + assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey())); + assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue())); + } + } + + /** * values collection contains all values */ public void testValues() { @@ -233,7 +218,7 @@ public class ConcurrentHashMapTest extends JSR166TestCase{ Iterator it = s.iterator(); while (it.hasNext()) { Map.Entry e = (Map.Entry) it.next(); - assertTrue( + assertTrue( (e.getKey().equals(one) && e.getValue().equals("A")) || (e.getKey().equals(two) && e.getValue().equals("B")) || (e.getKey().equals(three) && e.getValue().equals("C")) || @@ -357,12 +342,12 @@ public class ConcurrentHashMapTest extends JSR166TestCase{ for (int i = 1; i <= 5; ++i) { assertTrue(s.indexOf(String.valueOf(i)) >= 0); } - } + } // Exception tests - + /** - * Cannot create with negative capacity + * Cannot create with negative capacity */ public void testConstructor1() { try { @@ -561,6 +546,19 @@ public class ConcurrentHashMapTest extends JSR166TestCase{ } /** + * remove(x, null) returns false + */ + public void testRemove3() { + try { + ConcurrentHashMap c = new ConcurrentHashMap(5); + c.put("sadsdf", "asdads"); + assertFalse(c.remove("sadsdf", null)); + } catch(NullPointerException e){ + fail(); + } + } + + /** * A deserialized map equals original */ public void testSerialization() { diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/CyclicBarrierTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/CyclicBarrierTest.java index 9eac1a7..ecd6e45 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/CyclicBarrierTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/CyclicBarrierTest.java @@ -11,6 +11,8 @@ package tests.api.java.util.concurrent; import junit.framework.*; import java.util.*; import java.util.concurrent.*; +import java.util.concurrent.locks.*; +import java.util.concurrent.atomic.*; public class CyclicBarrierTest extends JSR166TestCase{ public static void main(String[] args) { @@ -164,7 +166,7 @@ public class CyclicBarrierTest extends JSR166TestCase{ * throw BrokenBarrierException */ public void testAwait2_Interrupted_BrokenBarrier() { - final CyclicBarrier c = new CyclicBarrier(3); + final CyclicBarrier c = new CyclicBarrier(3); Thread t1 = new Thread(new Runnable() { public void run() { try { @@ -229,7 +231,7 @@ public class CyclicBarrierTest extends JSR166TestCase{ * throw BrokenBarrierException */ public void testAwait4_Timeout_BrokenBarrier() { - final CyclicBarrier c = new CyclicBarrier(3); + final CyclicBarrier c = new CyclicBarrier(3); Thread t1 = new Thread(new Runnable() { public void run() { try { @@ -267,7 +269,7 @@ public class CyclicBarrierTest extends JSR166TestCase{ * throw BrokenBarrierException */ public void testAwait5_Timeout_BrokenBarrier() { - final CyclicBarrier c = new CyclicBarrier(3); + final CyclicBarrier c = new CyclicBarrier(3); Thread t1 = new Thread(new Runnable() { public void run() { try { @@ -376,4 +378,249 @@ public class CyclicBarrierTest extends JSR166TestCase{ } } + /** + * All threads block while a barrier is broken. + */ + public void testReset_Leakage() { + try { + final CyclicBarrier c = new CyclicBarrier(2); + final AtomicBoolean done = new AtomicBoolean(); + Thread t = new Thread() { + public void run() { + while (!done.get()) { + try { + while (c.isBroken()) + c.reset(); + + c.await(); + threadFail("await should not return"); + } + catch (BrokenBarrierException e) { + } + catch (InterruptedException ie) { + } + } + } + }; + + t.start(); + for( int i = 0; i < 4; i++) { + Thread.sleep(SHORT_DELAY_MS); + t.interrupt(); + } + done.set(true); + t.interrupt(); + } + catch (Exception ex) { + unexpectedException(); + } + } + + /** + * Reset of a non-broken barrier does not break barrier + */ + public void testResetWithoutBreakage() { + try { + final CyclicBarrier start = new CyclicBarrier(3); + final CyclicBarrier barrier = new CyclicBarrier(3); + for (int i = 0; i < 3; i++) { + Thread t1 = new Thread(new Runnable() { + public void run() { + try { start.await(); } + catch (Exception ie) { + threadFail("start barrier"); + } + try { barrier.await(); } + catch (Throwable thrown) { + unexpectedException(); + }}}); + + Thread t2 = new Thread(new Runnable() { + public void run() { + try { start.await(); } + catch (Exception ie) { + threadFail("start barrier"); + } + try { barrier.await(); } + catch (Throwable thrown) { + unexpectedException(); + }}}); + + + t1.start(); + t2.start(); + try { start.await(); } + catch (Exception ie) { threadFail("start barrier"); } + barrier.await(); + t1.join(); + t2.join(); + assertFalse(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + if (i == 1) barrier.reset(); + assertFalse(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + } + } + catch (Exception ex) { + unexpectedException(); + } + } + + /** + * Reset of a barrier after interruption reinitializes it. + */ + public void testResetAfterInterrupt() { + try { + final CyclicBarrier start = new CyclicBarrier(3); + final CyclicBarrier barrier = new CyclicBarrier(3); + for (int i = 0; i < 2; i++) { + Thread t1 = new Thread(new Runnable() { + public void run() { + try { start.await(); } + catch (Exception ie) { + threadFail("start barrier"); + } + try { barrier.await(); } + catch(InterruptedException ok) {} + catch (Throwable thrown) { + unexpectedException(); + }}}); + + Thread t2 = new Thread(new Runnable() { + public void run() { + try { start.await(); } + catch (Exception ie) { + threadFail("start barrier"); + } + try { barrier.await(); } + catch(BrokenBarrierException ok) {} + catch (Throwable thrown) { + unexpectedException(); + }}}); + + t1.start(); + t2.start(); + try { start.await(); } + catch (Exception ie) { threadFail("start barrier"); } + t1.interrupt(); + t1.join(); + t2.join(); + assertTrue(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + barrier.reset(); + assertFalse(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + } + } + catch (Exception ex) { + unexpectedException(); + } + } + + /** + * Reset of a barrier after timeout reinitializes it. + */ + public void testResetAfterTimeout() { + try { + final CyclicBarrier start = new CyclicBarrier(3); + final CyclicBarrier barrier = new CyclicBarrier(3); + for (int i = 0; i < 2; i++) { + Thread t1 = new Thread(new Runnable() { + public void run() { + try { start.await(); } + catch (Exception ie) { + threadFail("start barrier"); + } + try { barrier.await(MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS); } + catch(TimeoutException ok) {} + catch (Throwable thrown) { + unexpectedException(); + }}}); + + Thread t2 = new Thread(new Runnable() { + public void run() { + try { start.await(); } + catch (Exception ie) { + threadFail("start barrier"); + } + try { barrier.await(); } + catch(BrokenBarrierException ok) {} + catch (Throwable thrown) { + unexpectedException(); + }}}); + + t1.start(); + t2.start(); + try { start.await(); } + catch (Exception ie) { threadFail("start barrier"); } + t1.join(); + t2.join(); + assertTrue(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + barrier.reset(); + assertFalse(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + } + } + catch (Exception ex) { + unexpectedException(); + } + } + + + /** + * Reset of a barrier after a failed command reinitializes it. + */ + public void testResetAfterCommandException() { + try { + final CyclicBarrier start = new CyclicBarrier(3); + final CyclicBarrier barrier = + new CyclicBarrier(3, new Runnable() { + public void run() { + throw new NullPointerException(); }}); + for (int i = 0; i < 2; i++) { + Thread t1 = new Thread(new Runnable() { + public void run() { + try { start.await(); } + catch (Exception ie) { + threadFail("start barrier"); + } + try { barrier.await(); } + catch(BrokenBarrierException ok) {} + catch (Throwable thrown) { + unexpectedException(); + }}}); + + Thread t2 = new Thread(new Runnable() { + public void run() { + try { start.await(); } + catch (Exception ie) { + threadFail("start barrier"); + } + try { barrier.await(); } + catch(BrokenBarrierException ok) {} + catch (Throwable thrown) { + unexpectedException(); + }}}); + + t1.start(); + t2.start(); + try { start.await(); } + catch (Exception ie) { threadFail("start barrier"); } + while (barrier.getNumberWaiting() < 2) { Thread.yield(); } + try { barrier.await(); } + catch (Exception ok) { } + t1.join(); + t2.join(); + assertTrue(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + barrier.reset(); + assertFalse(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + } + } + catch (Exception ex) { + unexpectedException(); + } + } } diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/DelayQueueTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/DelayQueueTest.java index e332513..978edb4 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/DelayQueueTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/DelayQueueTest.java @@ -14,11 +14,11 @@ import java.util.concurrent.*; public class DelayQueueTest extends JSR166TestCase { public static void main(String[] args) { - junit.textui.TestRunner.run (suite()); + junit.textui.TestRunner.run (suite()); } public static Test suite() { - return new TestSuite(DelayQueueTest.class); + return new TestSuite(DelayQueueTest.class); } private static final int NOCAP = Integer.MAX_VALUE; @@ -27,20 +27,19 @@ public class DelayQueueTest extends JSR166TestCase { * A delayed implementation for testing. * Most tests use Pseudodelays, where delays are all elapsed * (so, no blocking solely for delays) but are still ordered - */ - static class PDelay implements Delayed { + */ + static class PDelay implements Delayed { int pseudodelay; PDelay(int i) { pseudodelay = Integer.MIN_VALUE + i; } - // BEGIN android-changed - public int compareTo(Delayed y) { + public int compareTo(PDelay y) { int i = pseudodelay; int j = ((PDelay)y).pseudodelay; if (i < j) return -1; if (i > j) return 1; return 0; } - // END android-changed - public int compareTo(PDelay y) { + + public int compareTo(Delayed y) { int i = pseudodelay; int j = ((PDelay)y).pseudodelay; if (i < j) return -1; @@ -72,21 +71,20 @@ public class DelayQueueTest extends JSR166TestCase { /** * Delayed implementation that actually delays */ - static class NanoDelay implements Delayed { + static class NanoDelay implements Delayed { long trigger; - NanoDelay(long i) { + NanoDelay(long i) { trigger = System.nanoTime() + i; } - // BEGIN android-changed - public int compareTo(Delayed y) { + public int compareTo(NanoDelay y) { long i = trigger; long j = ((NanoDelay)y).trigger; if (i < j) return -1; if (i > j) return 1; return 0; } - // END android-changed - public int compareTo(NanoDelay y) { + + public int compareTo(Delayed y) { long i = trigger; long j = ((NanoDelay)y).trigger; if (i < j) return -1; @@ -123,16 +121,16 @@ public class DelayQueueTest extends JSR166TestCase { private DelayQueue populatedQueue(int n) { DelayQueue q = new DelayQueue(); assertTrue(q.isEmpty()); - for(int i = n-1; i >= 0; i-=2) - assertTrue(q.offer(new PDelay(i))); - for(int i = (n & 1); i < n; i+=2) - assertTrue(q.offer(new PDelay(i))); + for(int i = n-1; i >= 0; i-=2) + assertTrue(q.offer(new PDelay(i))); + for(int i = (n & 1); i < n; i+=2) + assertTrue(q.offer(new PDelay(i))); assertFalse(q.isEmpty()); assertEquals(NOCAP, q.remainingCapacity()); - assertEquals(n, q.size()); + assertEquals(n, q.size()); return q; } - + /** * A new queue has unbounded capacity */ @@ -229,22 +227,22 @@ public class DelayQueueTest extends JSR166TestCase { * offer(null) throws NPE */ public void testOfferNull() { - try { + try { DelayQueue q = new DelayQueue(); q.offer(null); shouldThrow(); - } catch (NullPointerException success) { } + } catch (NullPointerException success) { } } /** * add(null) throws NPE */ public void testAddNull() { - try { + try { DelayQueue q = new DelayQueue(); q.add(null); shouldThrow(); - } catch (NullPointerException success) { } + } catch (NullPointerException success) { } } /** @@ -342,13 +340,13 @@ public class DelayQueueTest extends JSR166TestCase { * put(null) throws NPE */ public void testPutNull() { - try { + try { DelayQueue q = new DelayQueue(); q.put(null); shouldThrow(); - } + } catch (NullPointerException success){ - } + } } /** @@ -416,7 +414,7 @@ public class DelayQueueTest extends JSR166TestCase { } finally { } } }); - + try { t.start(); Thread.sleep(SMALL_DELAY_MS); @@ -431,14 +429,14 @@ public class DelayQueueTest extends JSR166TestCase { * take retrieves elements in priority order */ public void testTake() { - try { + try { DelayQueue q = populatedQueue(SIZE); for (int i = 0; i < SIZE; ++i) { assertEquals(new PDelay(i), ((PDelay)q.take())); } } catch (InterruptedException e){ - unexpectedException(); - } + unexpectedException(); + } } /** @@ -450,8 +448,8 @@ public class DelayQueueTest extends JSR166TestCase { public void run() { try { q.take(); - threadShouldThrow(); - } catch (InterruptedException success){ } + threadShouldThrow(); + } catch (InterruptedException success){ } } }); try { @@ -478,16 +476,16 @@ public class DelayQueueTest extends JSR166TestCase { q.take(); threadShouldThrow(); } catch (InterruptedException success){ - } + } }}); t.start(); - try { - Thread.sleep(SHORT_DELAY_MS); + try { + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); t.join(); } catch (InterruptedException ie) { - unexpectedException(); + unexpectedException(); } } @@ -500,7 +498,7 @@ public class DelayQueueTest extends JSR166TestCase { for (int i = 0; i < SIZE; ++i) { assertEquals(new PDelay(i), ((PDelay)q.poll())); } - assertNull(q.poll()); + assertNull(q.poll()); } /** @@ -514,8 +512,8 @@ public class DelayQueueTest extends JSR166TestCase { } assertNull(q.poll(0, TimeUnit.MILLISECONDS)); } catch (InterruptedException e){ - unexpectedException(); - } + unexpectedException(); + } } /** @@ -529,8 +527,8 @@ public class DelayQueueTest extends JSR166TestCase { } assertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); } catch (InterruptedException e){ - unexpectedException(); - } + unexpectedException(); + } } /** @@ -547,16 +545,16 @@ public class DelayQueueTest extends JSR166TestCase { } threadAssertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); } catch (InterruptedException success){ - } + } }}); t.start(); - try { - Thread.sleep(SHORT_DELAY_MS); + try { + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); t.join(); } catch (InterruptedException ie) { - unexpectedException(); + unexpectedException(); } } @@ -572,8 +570,8 @@ public class DelayQueueTest extends JSR166TestCase { threadAssertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); q.poll(LONG_DELAY_MS, TimeUnit.MILLISECONDS); q.poll(LONG_DELAY_MS, TimeUnit.MILLISECONDS); - threadFail("Should block"); - } catch (InterruptedException success) { } + threadFail("Should block"); + } catch (InterruptedException success) { } } }); try { @@ -585,7 +583,7 @@ public class DelayQueueTest extends JSR166TestCase { } catch (Exception e){ unexpectedException(); } - } + } /** @@ -596,10 +594,12 @@ public class DelayQueueTest extends JSR166TestCase { for (int i = 0; i < SIZE; ++i) { assertEquals(new PDelay(i), ((PDelay)q.peek())); q.poll(); - assertTrue(q.peek() == null || - i != ((PDelay)q.peek()).intValue()); + if (q.isEmpty()) + assertNull(q.peek()); + else + assertTrue(i != ((PDelay)q.peek()).intValue()); } - assertNull(q.peek()); + assertNull(q.peek()); } /** @@ -630,7 +630,7 @@ public class DelayQueueTest extends JSR166TestCase { q.remove(); shouldThrow(); } catch (NoSuchElementException success){ - } + } } /** @@ -647,7 +647,7 @@ public class DelayQueueTest extends JSR166TestCase { } assertTrue(q.isEmpty()); } - + /** * contains(x) reports true when elements added but not yet removed */ @@ -669,8 +669,10 @@ public class DelayQueueTest extends JSR166TestCase { assertTrue(q.isEmpty()); assertEquals(0, q.size()); assertEquals(NOCAP, q.remainingCapacity()); - q.add(new PDelay(1)); + PDelay x = new PDelay(1); + q.add(x); assertFalse(q.isEmpty()); + assertTrue(q.contains(x)); q.clear(); assertTrue(q.isEmpty()); } @@ -729,14 +731,14 @@ public class DelayQueueTest extends JSR166TestCase { */ public void testToArray() { DelayQueue q = populatedQueue(SIZE); - Object[] o = q.toArray(); + Object[] o = q.toArray(); Arrays.sort(o); - try { - for(int i = 0; i < o.length; i++) - assertEquals(o[i], q.take()); - } catch (InterruptedException e){ - unexpectedException(); - } + try { + for(int i = 0; i < o.length; i++) + assertEquals(o[i], q.take()); + } catch (InterruptedException e){ + unexpectedException(); + } } /** @@ -744,15 +746,15 @@ public class DelayQueueTest extends JSR166TestCase { */ public void testToArray2() { DelayQueue q = populatedQueue(SIZE); - PDelay[] ints = new PDelay[SIZE]; - ints = (PDelay[])q.toArray(ints); + PDelay[] ints = new PDelay[SIZE]; + ints = (PDelay[])q.toArray(ints); Arrays.sort(ints); - try { - for(int i = 0; i < ints.length; i++) - assertEquals(ints[i], q.take()); - } catch (InterruptedException e){ - unexpectedException(); - } + try { + for(int i = 0; i < ints.length; i++) + assertEquals(ints[i], q.take()); + } catch (InterruptedException e){ + unexpectedException(); + } } @@ -760,31 +762,31 @@ public class DelayQueueTest extends JSR166TestCase { * toArray(null) throws NPE */ public void testToArray_BadArg() { - try { + try { DelayQueue q = populatedQueue(SIZE); - Object o[] = q.toArray(null); - shouldThrow(); - } catch(NullPointerException success){} + Object o[] = q.toArray(null); + shouldThrow(); + } catch(NullPointerException success){} } /** * toArray with incompatible array type throws CCE */ public void testToArray1_BadArg() { - try { + try { DelayQueue q = populatedQueue(SIZE); - Object o[] = q.toArray(new String[10] ); - shouldThrow(); - } catch(ArrayStoreException success){} + Object o[] = q.toArray(new String[10] ); + shouldThrow(); + } catch(ArrayStoreException success){} } - + /** * iterator iterates through all elements */ public void testIterator() { DelayQueue q = populatedQueue(SIZE); int i = 0; - Iterator it = q.iterator(); + Iterator it = q.iterator(); while(it.hasNext()) { assertTrue(q.contains(it.next())); ++i; @@ -819,7 +821,7 @@ public class DelayQueueTest extends JSR166TestCase { for (int i = 0; i < SIZE; ++i) { assertTrue(s.indexOf(String.valueOf(Integer.MIN_VALUE+i)) >= 0); } - } + } /** * offer transfers elements across Executor tasks @@ -875,7 +877,7 @@ public class DelayQueueTest extends JSR166TestCase { NanoDelay e = (NanoDelay)(q.take()); long tt = e.getTriggerTime(); assertTrue(tt <= System.nanoTime()); - if (i != 0) + if (i != 0) assertTrue(tt >= last); last = tt; } @@ -885,10 +887,41 @@ public class DelayQueueTest extends JSR166TestCase { } } + /** + * peek of a non-empty queue returns non-null even if not expired + */ + public void testPeekDelayed() { + DelayQueue q = new DelayQueue(); + q.add(new NanoDelay(Long.MAX_VALUE)); + assert(q.peek() != null); + } + + + /** + * poll of a non-empty queue returns null if no expired elements. + */ + public void testPollDelayed() { + DelayQueue q = new DelayQueue(); + q.add(new NanoDelay(Long.MAX_VALUE)); + assertNull(q.poll()); + } + + /** + * timed poll of a non-empty queue returns null if no expired elements. + */ + public void testTimedPollDelayed() { + DelayQueue q = new DelayQueue(); + q.add(new NanoDelay(LONG_DELAY_MS * 1000000L)); + try { + assertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); + } catch (Exception ex) { + unexpectedException(); + } + } /** * drainTo(null) throws NPE - */ + */ public void testDrainToNull() { DelayQueue q = populatedQueue(SIZE); try { @@ -900,7 +933,7 @@ public class DelayQueueTest extends JSR166TestCase { /** * drainTo(this) throws IAE - */ + */ public void testDrainToSelf() { DelayQueue q = populatedQueue(SIZE); try { @@ -912,13 +945,30 @@ public class DelayQueueTest extends JSR166TestCase { /** * drainTo(c) empties queue into another collection c - */ + */ public void testDrainTo() { - DelayQueue q = populatedQueue(SIZE); + DelayQueue q = new DelayQueue(); + PDelay[] elems = new PDelay[SIZE]; + for (int i = 0; i < SIZE; ++i) { + elems[i] = new PDelay(i); + q.add(elems[i]); + } ArrayList l = new ArrayList(); q.drainTo(l); assertEquals(q.size(), 0); - assertEquals(l.size(), SIZE); + for (int i = 0; i < SIZE; ++i) + assertEquals(l.get(i), elems[i]); + q.add(elems[0]); + q.add(elems[1]); + assertFalse(q.isEmpty()); + assertTrue(q.contains(elems[0])); + assertTrue(q.contains(elems[1])); + l.clear(); + q.drainTo(l); + assertEquals(q.size(), 0); + assertEquals(l.size(), 2); + for (int i = 0; i < 2; ++i) + assertEquals(l.get(i), elems[i]); } /** diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorCompletionServiceTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorCompletionServiceTest.java index 2e237e2..b1988cc 100644 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorCompletionServiceTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorCompletionServiceTest.java @@ -11,6 +11,7 @@ package tests.api.java.util.concurrent; import junit.framework.*; import java.util.*; import java.util.concurrent.*; +import java.util.concurrent.atomic.*; import java.math.BigInteger; import java.security.*; @@ -88,7 +89,7 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase{ Callable c = new StringTask(); ecs.submit(c); Future f = ecs.take(); - assert(f.isDone()); + assertTrue(f.isDone()); } catch (Exception ex) { unexpectedException(); } finally { @@ -128,7 +129,7 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase{ for (;;) { Future f = ecs.poll(); if (f != null) { - assert(f.isDone()); + assertTrue(f.isDone()); break; } } @@ -151,12 +152,11 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase{ ecs.submit(c); Future f = ecs.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS); if (f != null) - assert(f.isDone()); + assertTrue(f.isDone()); } catch (Exception ex) { unexpectedException(); } finally { joinPool(e); } } - } diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorsTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorsTest.java index 40e23e4..e8fc7e5 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorsTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorsTest.java @@ -16,7 +16,7 @@ import java.security.*; public class ExecutorsTest extends JSR166TestCase{ public static void main(String[] args) { - junit.textui.TestRunner.run (suite()); + junit.textui.TestRunner.run (suite()); } public static Test suite() { return new TestSuite(ExecutorsTest.class); @@ -26,13 +26,13 @@ public class ExecutorsTest extends JSR166TestCase{ private final ExecutorService exec; private final Callable<T> func; private final long msecs; - + TimedCallable(ExecutorService exec, Callable<T> func, long msecs) { this.exec = exec; this.func = func; this.msecs = msecs; } - + public T call() throws Exception { Future<T> ftask = exec.submit(func); try { @@ -295,13 +295,13 @@ public class ExecutorsTest extends JSR166TestCase{ List<Callable<BigInteger>> tasks = new ArrayList<Callable<BigInteger>>(N); try { long startTime = System.currentTimeMillis(); - + long i = 0; while (tasks.size() < N) { tasks.add(new TimedCallable<BigInteger>(executor, new Fib(i), 1)); i += 10; } - + int iters = 0; BigInteger sum = BigInteger.ZERO; for (Iterator<Callable<BigInteger>> it = tasks.iterator(); it.hasNext();) { @@ -326,7 +326,7 @@ public class ExecutorsTest extends JSR166TestCase{ } } - + /** * ThreadPoolExecutor using defaultThreadFactory has * specified group, priority, daemon status, and name @@ -335,31 +335,31 @@ public class ExecutorsTest extends JSR166TestCase{ final ThreadGroup egroup = Thread.currentThread().getThreadGroup(); Runnable r = new Runnable() { public void run() { - try { - Thread current = Thread.currentThread(); - threadAssertTrue(!current.isDaemon()); - threadAssertTrue(current.getPriority() == Thread.NORM_PRIORITY); - ThreadGroup g = current.getThreadGroup(); - SecurityManager s = System.getSecurityManager(); - if (s != null) - threadAssertTrue(g == s.getThreadGroup()); - else - threadAssertTrue(g == egroup); - String name = current.getName(); - threadAssertTrue(name.endsWith("thread-1")); - } catch (SecurityException ok) { - // Also pass if not allowed to change setting - } + try { + Thread current = Thread.currentThread(); + threadAssertTrue(!current.isDaemon()); + threadAssertTrue(current.getPriority() <= Thread.NORM_PRIORITY); + ThreadGroup g = current.getThreadGroup(); + SecurityManager s = System.getSecurityManager(); + if (s != null) + threadAssertTrue(g == s.getThreadGroup()); + else + threadAssertTrue(g == egroup); + String name = current.getName(); + threadAssertTrue(name.endsWith("thread-1")); + } catch (SecurityException ok) { + // Also pass if not allowed to change setting + } } }; ExecutorService e = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory()); - + e.execute(r); try { e.shutdown(); } catch(SecurityException ok) { } - + try { Thread.sleep(SHORT_DELAY_MS); } catch (Exception eX) { @@ -390,27 +390,27 @@ public class ExecutorsTest extends JSR166TestCase{ final AccessControlContext thisacc = AccessController.getContext(); Runnable r = new Runnable() { public void run() { - try { - Thread current = Thread.currentThread(); - threadAssertTrue(!current.isDaemon()); - threadAssertTrue(current.getPriority() == Thread.NORM_PRIORITY); - ThreadGroup g = current.getThreadGroup(); - SecurityManager s = System.getSecurityManager(); - if (s != null) - threadAssertTrue(g == s.getThreadGroup()); - else - threadAssertTrue(g == egroup); - String name = current.getName(); - threadAssertTrue(name.endsWith("thread-1")); - threadAssertTrue(thisccl == current.getContextClassLoader()); - threadAssertTrue(thisacc.equals(AccessController.getContext())); - } catch(SecurityException ok) { - // Also pass if not allowed to change settings - } + try { + Thread current = Thread.currentThread(); + threadAssertTrue(!current.isDaemon()); + threadAssertTrue(current.getPriority() <= Thread.NORM_PRIORITY); + ThreadGroup g = current.getThreadGroup(); + SecurityManager s = System.getSecurityManager(); + if (s != null) + threadAssertTrue(g == s.getThreadGroup()); + else + threadAssertTrue(g == egroup); + String name = current.getName(); + threadAssertTrue(name.endsWith("thread-1")); + threadAssertTrue(thisccl == current.getContextClassLoader()); + threadAssertTrue(thisacc.equals(AccessController.getContext())); + } catch(SecurityException ok) { + // Also pass if not allowed to change settings + } } }; ExecutorService e = Executors.newSingleThreadExecutor(Executors.privilegedThreadFactory()); - + Policy.setPolicy(savedPolicy); e.execute(r); try { @@ -460,7 +460,7 @@ public class ExecutorsTest extends JSR166TestCase{ Policy.setPolicy(savedPolicy); return; } catch(AccessControlException ok) { - } + } try { Callable task = Executors.privilegedCallableUsingCurrentClassLoader(new NoOpCallable()); @@ -468,7 +468,7 @@ public class ExecutorsTest extends JSR166TestCase{ } catch(AccessControlException success) { } catch(Exception ex) { unexpectedException(); - } + } finally { Policy.setPolicy(savedPolicy); } @@ -489,13 +489,13 @@ public class ExecutorsTest extends JSR166TestCase{ } catch (AccessControlException ok) { return; } - + try { Callable task = Executors.privilegedCallableUsingCurrentClassLoader(new NoOpCallable()); task.call(); } catch(Exception ex) { unexpectedException(); - } + } finally { Policy.setPolicy(savedPolicy); } @@ -520,7 +520,7 @@ public class ExecutorsTest extends JSR166TestCase{ return; // program has too few permissions to set up test } - // Make sure that program doesn't have too many permissions + // Make sure that program doesn't have too many permissions try { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { @@ -538,7 +538,7 @@ public class ExecutorsTest extends JSR166TestCase{ } catch(AccessControlException success) { } catch(Exception ex) { unexpectedException(); - } + } } /** diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/JSR166TestCase.java b/concurrent/src/test/java/tests/api/java/util/concurrent/JSR166TestCase.java index 70dafbc..c900616 100644 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/JSR166TestCase.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/JSR166TestCase.java @@ -2,8 +2,8 @@ * 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 - * Other contributors include Andrew Wright, Jeffrey Hayes, - * Pat Fisher, Mike Judd. + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. */ package tests.api.java.util.concurrent; @@ -19,13 +19,13 @@ import java.security.*; * utility methods and classes, as well as a simple framework for * helping to make sure that assertions failing in generated threads * cause the associated test that generated them to itself fail (which - * JUnit doe not otherwise arrange). The rules for creating such + * JUnit does not otherwise arrange). The rules for creating such * tests are: * * <ol> * * <li> All assertions in code running in generated threads must use - * the forms {@link #threadFail} , {@link #threadAssertTrue} {@link + * the forms {@link #threadFail}, {@link #threadAssertTrue}, {@link * #threadAssertEquals}, or {@link #threadAssertNull}, (not * <tt>fail</tt>, <tt>assertTrue</tt>, etc.) It is OK (but not * particularly recommended) for other code to use these forms too. @@ -46,7 +46,7 @@ import java.security.*; * is always discriminable as larger than SHORT and smaller than * MEDIUM. And so on. These constants are set to conservative values, * but even so, if there is ever any doubt, they can all be increased - * in one spot to rerun tests on slower platforms</li> + * in one spot to rerun tests on slower platforms.</li> * * <li> All threads generated must be joined inside each test case * method (or <tt>fail</tt> to do so) before returning from the @@ -65,7 +65,7 @@ import java.security.*; * "normal" behaviors differ significantly. And sometimes testcases * cover multiple methods when they cannot be tested in * isolation.</li> - * + * * <li> The documentation style for testcases is to provide as javadoc * a simple sentence or two describing the property that the testcase * method purports to test. The javadocs do not say anything about how @@ -90,10 +90,10 @@ import java.security.*; public class JSR166TestCase extends TestCase { /** * Runs all JSR166 unit tests using junit.textui.TestRunner - */ + */ public static void main (String[] args) { int iters = 1; - if (args.length > 0) + if (args.length > 0) iters = Integer.parseInt(args[0]); Test s = suite(); for (int i = 0; i < iters; ++i) { @@ -106,7 +106,7 @@ public class JSR166TestCase extends TestCase { /** * Collects all JSR166 unit tests as one suite - */ + */ public static Test suite ( ) { TestSuite suite = tests.TestSuiteFactory.createTestSuite("JSR166 Unit Tests"); // BEGIN android-changed @@ -114,18 +114,18 @@ public class JSR166TestCase extends TestCase { suite.addTest(AbstractQueueTest.suite()); suite.addTest(AbstractQueuedSynchronizerTest.suite()); suite.addTest(ArrayBlockingQueueTest.suite()); - suite.addTest(AtomicBooleanTest.suite()); - suite.addTest(AtomicIntegerArrayTest.suite()); - suite.addTest(AtomicIntegerFieldUpdaterTest.suite()); - suite.addTest(AtomicIntegerTest.suite()); - suite.addTest(AtomicLongArrayTest.suite()); - suite.addTest(AtomicLongFieldUpdaterTest.suite()); - suite.addTest(AtomicLongTest.suite()); - suite.addTest(AtomicMarkableReferenceTest.suite()); - suite.addTest(AtomicReferenceArrayTest.suite()); - suite.addTest(AtomicReferenceFieldUpdaterTest.suite()); - suite.addTest(AtomicReferenceTest.suite()); - suite.addTest(AtomicStampedReferenceTest.suite()); + suite.addTest(AtomicBooleanTest.suite()); + suite.addTest(AtomicIntegerArrayTest.suite()); + suite.addTest(AtomicIntegerFieldUpdaterTest.suite()); + suite.addTest(AtomicIntegerTest.suite()); + suite.addTest(AtomicLongArrayTest.suite()); + suite.addTest(AtomicLongFieldUpdaterTest.suite()); + suite.addTest(AtomicLongTest.suite()); + suite.addTest(AtomicMarkableReferenceTest.suite()); + suite.addTest(AtomicReferenceArrayTest.suite()); + suite.addTest(AtomicReferenceFieldUpdaterTest.suite()); + suite.addTest(AtomicReferenceTest.suite()); + suite.addTest(AtomicStampedReferenceTest.suite()); suite.addTest(ConcurrentHashMapTest.suite()); suite.addTest(ConcurrentLinkedQueueTest.suite()); suite.addTest(CopyOnWriteArrayListTest.suite()); @@ -164,16 +164,16 @@ public class JSR166TestCase extends TestCase { /** - * Return the shortest timed delay. This could + * Returns the shortest timed delay. This could * be reimplemented to use for example a Property. - */ + */ protected long getShortDelay() { return 50; } /** - * Set delays as multiples of SHORT_DELAY. + * Sets delays as multiples of SHORT_DELAY. */ protected void setDelays() { SHORT_DELAY_MS = getShortDelay(); @@ -188,23 +188,23 @@ public class JSR166TestCase extends TestCase { volatile boolean threadFailed; /** - * Initialize test to indicate that no thread assertions have failed + * Initializes test to indicate that no thread assertions have failed */ - public void setUp() { + public void setUp() { setDelays(); - threadFailed = false; + threadFailed = false; } /** - * Trigger test case failure if any thread assertions have failed + * Triggers test case failure if any thread assertions have failed */ - public void tearDown() { - assertFalse(threadFailed); + public void tearDown() { + assertFalse(threadFailed); } /** * Fail, also setting status to indicate current testcase should fail - */ + */ public void threadFail(String reason) { threadFailed = true; fail(reason); @@ -213,7 +213,7 @@ public class JSR166TestCase extends TestCase { /** * If expression not true, set status to indicate current testcase * should fail - */ + */ public void threadAssertTrue(boolean b) { if (!b) { threadFailed = true; @@ -224,7 +224,7 @@ public class JSR166TestCase extends TestCase { /** * If expression not false, set status to indicate current testcase * should fail - */ + */ public void threadAssertFalse(boolean b) { if (b) { threadFailed = true; @@ -235,7 +235,7 @@ public class JSR166TestCase extends TestCase { /** * If argument not null, set status to indicate current testcase * should fail - */ + */ public void threadAssertNull(Object x) { if (x != null) { threadFailed = true; @@ -246,7 +246,7 @@ public class JSR166TestCase extends TestCase { /** * If arguments not equal, set status to indicate current testcase * should fail - */ + */ public void threadAssertEquals(long x, long y) { if (x != y) { threadFailed = true; @@ -257,7 +257,7 @@ public class JSR166TestCase extends TestCase { /** * If arguments not equal, set status to indicate current testcase * should fail - */ + */ public void threadAssertEquals(Object x, Object y) { if (x != y && (x == null || !x.equals(y))) { threadFailed = true; @@ -267,10 +267,15 @@ public class JSR166TestCase extends TestCase { /** * threadFail with message "should throw exception" - */ + */ public void threadShouldThrow() { - threadFailed = true; - fail("should throw exception"); + try { + threadFailed = true; + fail("should throw exception"); + } catch (AssertionFailedError e) { + e.printStackTrace(); + throw e; + } } /** @@ -281,6 +286,14 @@ public class JSR166TestCase extends TestCase { fail("Unexpected exception"); } + /** + * threadFail with message "Unexpected exception", with argument + */ + public void threadUnexpectedException(Throwable ex) { + threadFailed = true; + ex.printStackTrace(); + fail("Unexpected exception: " + ex); + } /** * Wait out termination of a thread pool or fail doing so @@ -299,7 +312,7 @@ public class JSR166TestCase extends TestCase { /** * fail with message "should throw exception" - */ + */ public void shouldThrow() { fail("Should throw exception"); } @@ -334,6 +347,7 @@ public class JSR166TestCase extends TestCase { static final Integer m3 = new Integer(-3); static final Integer m4 = new Integer(-4); static final Integer m5 = new Integer(-5); + static final Integer m6 = new Integer(-6); static final Integer m10 = new Integer(-10); @@ -389,7 +403,7 @@ public class JSR166TestCase extends TestCase { Thread.sleep(SHORT_DELAY_MS); } catch(Exception e) { - threadUnexpectedException(); + threadUnexpectedException(e); } } } @@ -411,7 +425,7 @@ public class JSR166TestCase extends TestCase { Thread.sleep(SMALL_DELAY_MS); } catch(Exception e) { - threadUnexpectedException(); + threadUnexpectedException(e); } } } @@ -432,7 +446,7 @@ public class JSR166TestCase extends TestCase { Thread.sleep(SMALL_DELAY_MS); } catch(Exception e) { - threadUnexpectedException(); + threadUnexpectedException(e); } return Boolean.TRUE; } @@ -456,7 +470,7 @@ public class JSR166TestCase extends TestCase { Thread.sleep(MEDIUM_DELAY_MS); } catch(Exception e) { - threadUnexpectedException(); + threadUnexpectedException(e); } } } @@ -498,7 +512,7 @@ public class JSR166TestCase extends TestCase { static class SimpleThreadFactory implements ThreadFactory{ public Thread newThread(Runnable r){ return new Thread(r); - } + } } static class TrackedShortRunnable implements Runnable { @@ -558,8 +572,8 @@ public class JSR166TestCase extends TestCase { * For use as RejectedExecutionHandler in constructors */ static class NoOpREHandler implements RejectedExecutionHandler{ - public void rejectedExecution(Runnable r, ThreadPoolExecutor executor){} + public void rejectedExecution(Runnable r, ThreadPoolExecutor executor){} } - - + + } diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/LinkedBlockingQueueTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/LinkedBlockingQueueTest.java index 7496a4a..6648afb 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/LinkedBlockingQueueTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/LinkedBlockingQueueTest.java @@ -16,7 +16,7 @@ import java.io.*; public class LinkedBlockingQueueTest extends JSR166TestCase { public static void main(String[] args) { - junit.textui.TestRunner.run (suite()); + junit.textui.TestRunner.run (suite()); } public static Test suite() { @@ -38,7 +38,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { assertEquals(n, q.size()); return q; } - + /** * A new queue has the indicated capacity, or Integer.MAX_VALUE if * none given @@ -151,7 +151,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { LinkedBlockingQueue q = new LinkedBlockingQueue(1); q.offer(null); shouldThrow(); - } catch (NullPointerException success) { } + } catch (NullPointerException success) { } } /** @@ -162,7 +162,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { LinkedBlockingQueue q = new LinkedBlockingQueue(1); q.add(null); shouldThrow(); - } catch (NullPointerException success) { } + } catch (NullPointerException success) { } } /** @@ -186,7 +186,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { assertEquals(0, q.remainingCapacity()); q.add(new Integer(SIZE)); } catch (IllegalStateException success){ - } + } } /** @@ -280,9 +280,9 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE); q.put(null); shouldThrow(); - } + } catch (NullPointerException success){ - } + } catch (InterruptedException ie) { unexpectedException(); } @@ -323,11 +323,11 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { threadShouldThrow(); } catch (InterruptedException ie){ threadAssertEquals(added, SIZE); - } + } }}); t.start(); - try { - Thread.sleep(SHORT_DELAY_MS); + try { + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); t.join(); } @@ -386,7 +386,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } catch (InterruptedException success){} } }); - + try { t.start(); Thread.sleep(SMALL_DELAY_MS); @@ -408,7 +408,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } } catch (InterruptedException e){ unexpectedException(); - } + } } /** @@ -421,7 +421,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { try { q.take(); threadShouldThrow(); - } catch (InterruptedException success){ } + } catch (InterruptedException success){ } } }); try { @@ -448,11 +448,11 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { q.take(); threadShouldThrow(); } catch (InterruptedException success){ - } + } }}); t.start(); - try { - Thread.sleep(SHORT_DELAY_MS); + try { + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); t.join(); } @@ -485,7 +485,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { assertNull(q.poll(0, TimeUnit.MILLISECONDS)); } catch (InterruptedException e){ unexpectedException(); - } + } } /** @@ -500,7 +500,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { assertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); } catch (InterruptedException e){ unexpectedException(); - } + } } /** @@ -517,11 +517,11 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } threadAssertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); } catch (InterruptedException success){ - } + } }}); t.start(); - try { - Thread.sleep(SHORT_DELAY_MS); + try { + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); t.join(); } @@ -543,7 +543,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { q.poll(LONG_DELAY_MS, TimeUnit.MILLISECONDS); q.poll(LONG_DELAY_MS, TimeUnit.MILLISECONDS); threadShouldThrow(); - } catch (InterruptedException success) { } + } catch (InterruptedException success) { } } }); try { @@ -555,7 +555,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } catch (Exception e){ unexpectedException(); } - } + } /** * peek returns next element, or null if empty @@ -599,7 +599,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { q.remove(); shouldThrow(); } catch (NoSuchElementException success){ - } + } } /** @@ -616,7 +616,24 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } assertTrue(q.isEmpty()); } - + + /** + * An add following remove(x) succeeds + */ + public void testRemoveElementAndAdd() { + try { + LinkedBlockingQueue q = new LinkedBlockingQueue(); + assertTrue(q.add(new Integer(1))); + assertTrue(q.add(new Integer(2))); + assertTrue(q.remove(new Integer(1))); + assertTrue(q.remove(new Integer(2))); + assertTrue(q.add(new Integer(3))); + assertTrue(q.take() != null); + } catch (Exception e){ + unexpectedException(); + } + } + /** * contains(x) reports true when elements added but not yet removed */ @@ -640,6 +657,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { assertEquals(SIZE, q.remainingCapacity()); q.add(one); assertFalse(q.isEmpty()); + assertTrue(q.contains(one)); q.clear(); assertTrue(q.isEmpty()); } @@ -704,7 +722,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { assertEquals(o[i], q.take()); } catch (InterruptedException e){ unexpectedException(); - } + } } /** @@ -719,7 +737,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { assertEquals(ints[i], q.take()); } catch (InterruptedException e){ unexpectedException(); - } + } } /** @@ -744,7 +762,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } catch(ArrayStoreException success){} } - + /** * iterator iterates through all elements */ @@ -757,7 +775,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } } catch (InterruptedException e){ unexpectedException(); - } + } } /** @@ -772,7 +790,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { Iterator it = q.iterator(); it.next(); it.remove(); - + it = q.iterator(); assertEquals(it.next(), one); assertEquals(it.next(), three); @@ -827,7 +845,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { for (int i = 0; i < SIZE; ++i) { assertTrue(s.indexOf(String.valueOf(i)) >= 0); } - } + } /** @@ -862,7 +880,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } } }); - + joinPool(executor); } @@ -896,7 +914,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } } }); - + joinPool(executor); } @@ -916,7 +934,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(bin)); LinkedBlockingQueue r = (LinkedBlockingQueue)in.readObject(); assertEquals(q.size(), r.size()); - while (!q.isEmpty()) + while (!q.isEmpty()) assertEquals(q.remove(), r.remove()); } catch(Exception e){ unexpectedException(); @@ -925,7 +943,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { /** * drainTo(null) throws NPE - */ + */ public void testDrainToNull() { LinkedBlockingQueue q = populatedQueue(SIZE); try { @@ -937,7 +955,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { /** * drainTo(this) throws IAE - */ + */ public void testDrainToSelf() { LinkedBlockingQueue q = populatedQueue(SIZE); try { @@ -949,27 +967,38 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { /** * drainTo(c) empties queue into another collection c - */ + */ public void testDrainTo() { LinkedBlockingQueue q = populatedQueue(SIZE); ArrayList l = new ArrayList(); q.drainTo(l); assertEquals(q.size(), 0); assertEquals(l.size(), SIZE); - for (int i = 0; i < SIZE; ++i) + for (int i = 0; i < SIZE; ++i) + assertEquals(l.get(i), new Integer(i)); + q.add(zero); + q.add(one); + assertFalse(q.isEmpty()); + assertTrue(q.contains(zero)); + assertTrue(q.contains(one)); + l.clear(); + q.drainTo(l); + assertEquals(q.size(), 0); + assertEquals(l.size(), 2); + for (int i = 0; i < 2; ++i) assertEquals(l.get(i), new Integer(i)); } /** * drainTo empties full queue, unblocking a waiting put. - */ + */ public void testDrainToWithActivePut() { final LinkedBlockingQueue q = populatedQueue(SIZE); Thread t = new Thread(new Runnable() { public void run() { try { q.put(new Integer(SIZE+1)); - } catch (InterruptedException ie){ + } catch (InterruptedException ie){ threadUnexpectedException(); } } @@ -979,7 +1008,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { ArrayList l = new ArrayList(); q.drainTo(l); assertTrue(l.size() >= SIZE); - for (int i = 0; i < SIZE; ++i) + for (int i = 0; i < SIZE; ++i) assertEquals(l.get(i), new Integer(i)); t.join(); assertTrue(q.size() + l.size() >= SIZE); @@ -990,7 +1019,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { /** * drainTo(null, n) throws NPE - */ + */ public void testDrainToNullN() { LinkedBlockingQueue q = populatedQueue(SIZE); try { @@ -1002,7 +1031,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { /** * drainTo(this, n) throws IAE - */ + */ public void testDrainToSelfN() { LinkedBlockingQueue q = populatedQueue(SIZE); try { @@ -1014,17 +1043,20 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { /** * drainTo(c, n) empties first max {n, size} elements of queue into c - */ + */ public void testDrainToN() { + LinkedBlockingQueue q = new LinkedBlockingQueue(); for (int i = 0; i < SIZE + 2; ++i) { - LinkedBlockingQueue q = populatedQueue(SIZE); + for(int j = 0; j < SIZE; j++) + assertTrue(q.offer(new Integer(j))); ArrayList l = new ArrayList(); q.drainTo(l, i); int k = (i < SIZE)? i : SIZE; - assertEquals(q.size(), SIZE-k); assertEquals(l.size(), k); - for (int j = 0; j < k; ++j) + assertEquals(q.size(), SIZE-k); + for (int j = 0; j < k; ++j) assertEquals(l.get(j), new Integer(j)); + while (q.poll() != null) ; } } diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/LockSupportTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/LockSupportTest.java index 8d74a3a..b39db2e 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/LockSupportTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/LockSupportTest.java @@ -15,7 +15,7 @@ import java.util.concurrent.locks.*; public class LockSupportTest extends JSR166TestCase{ public static void main(String[] args) { - junit.textui.TestRunner.run (suite()); + junit.textui.TestRunner.run (suite()); } public static Test suite() { return new TestSuite(LockSupportTest.class); @@ -24,7 +24,7 @@ public class LockSupportTest extends JSR166TestCase{ /** * park is released by unpark occurring after park */ - public void testPark() { + public void testPark() { Thread t = new Thread(new Runnable() { public void run() { try { @@ -48,7 +48,7 @@ public class LockSupportTest extends JSR166TestCase{ /** * park is released by unpark occurring before park */ - public void testPark2() { + public void testPark2() { Thread t = new Thread(new Runnable() { public void run() { try { @@ -70,15 +70,14 @@ public class LockSupportTest extends JSR166TestCase{ } /** - * park is released by interrupt + * park is released by interrupt */ - public void testPark3() { - Thread t = new Thread(new Runnable() { - public void run() { - try { - LockSupport.park(); - threadAssertTrue(Thread.interrupted()); - } catch(Exception e){ + public void testPark3() { + Thread t = new Thread(new Runnable() { + public void run() { + try { + LockSupport.park(); + } catch(Exception e){ threadUnexpectedException(); } } @@ -97,7 +96,7 @@ public class LockSupportTest extends JSR166TestCase{ /** * park returns if interrupted before park */ - public void testPark4() { + public void testPark4() { final ReentrantLock lock = new ReentrantLock(); lock.lock(); Thread t = new Thread(new Runnable() { @@ -124,7 +123,7 @@ public class LockSupportTest extends JSR166TestCase{ /** * parkNanos times out if not unparked */ - public void testParkNanos() { + public void testParkNanos() { Thread t = new Thread(new Runnable() { public void run() { try { @@ -147,7 +146,7 @@ public class LockSupportTest extends JSR166TestCase{ /** * parkUntil times out if not unparked */ - public void testParkUntil() { + public void testParkUntil() { Thread t = new Thread(new Runnable() { public void run() { try { diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/PriorityBlockingQueueTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/PriorityBlockingQueueTest.java index b258963..3857e0f 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/PriorityBlockingQueueTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/PriorityBlockingQueueTest.java @@ -628,9 +628,9 @@ public class PriorityBlockingQueueTest extends JSR166TestCase { q.clear(); assertTrue(q.isEmpty()); assertEquals(0, q.size()); - assertEquals(NOCAP, q.remainingCapacity()); - q.add(new Integer(1)); + q.add(one); assertFalse(q.isEmpty()); + assertTrue(q.contains(one)); q.clear(); assertTrue(q.isEmpty()); } @@ -873,6 +873,17 @@ public class PriorityBlockingQueueTest extends JSR166TestCase { assertEquals(l.size(), SIZE); for (int i = 0; i < SIZE; ++i) assertEquals(l.get(i), new Integer(i)); + q.add(zero); + q.add(one); + assertFalse(q.isEmpty()); + assertTrue(q.contains(zero)); + assertTrue(q.contains(one)); + l.clear(); + q.drainTo(l); + assertEquals(q.size(), 0); + assertEquals(l.size(), 2); + for (int i = 0; i < 2; ++i) + assertEquals(l.get(i), new Integer(i)); } /** @@ -927,15 +938,18 @@ public class PriorityBlockingQueueTest extends JSR166TestCase { * drainTo(c, n) empties first max {n, size} elements of queue into c */ public void testDrainToN() { + PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE*2); for (int i = 0; i < SIZE + 2; ++i) { - PriorityBlockingQueue q = populatedQueue(SIZE); + for(int j = 0; j < SIZE; j++) + assertTrue(q.offer(new Integer(j))); ArrayList l = new ArrayList(); q.drainTo(l, i); int k = (i < SIZE)? i : SIZE; - assertEquals(q.size(), SIZE-k); assertEquals(l.size(), k); + assertEquals(q.size(), SIZE-k); for (int j = 0; j < k; ++j) - assertTrue(l.contains(new Integer(j))); + assertEquals(l.get(j), new Integer(j)); + while (q.poll() != null) ; } } diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantLockTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantLockTest.java index fdc9f31..c50482d 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantLockTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantLockTest.java @@ -16,7 +16,7 @@ import java.io.*; public class ReentrantLockTest extends JSR166TestCase { public static void main(String[] args) { - junit.textui.TestRunner.run (suite()); + junit.textui.TestRunner.run (suite()); } public static Test suite() { return new TestSuite(ReentrantLockTest.class); @@ -56,11 +56,11 @@ public class ReentrantLockTest extends JSR166TestCase { */ static class PublicReentrantLock extends ReentrantLock { PublicReentrantLock() { super(); } - public Collection<Thread> getQueuedThreads() { - return super.getQueuedThreads(); + public Collection<Thread> getQueuedThreads() { + return super.getQueuedThreads(); } - public Collection<Thread> getWaitingThreads(Condition c) { - return super.getWaitingThreads(c); + public Collection<Thread> getWaitingThreads(Condition c) { + return super.getWaitingThreads(c); } @@ -69,7 +69,7 @@ public class ReentrantLockTest extends JSR166TestCase { /** * Constructor sets given fairness */ - public void testConstructor() { + public void testConstructor() { ReentrantLock rl = new ReentrantLock(); assertFalse(rl.isFair()); ReentrantLock r2 = new ReentrantLock(true); @@ -79,7 +79,7 @@ public class ReentrantLockTest extends JSR166TestCase { /** * locking an unlocked lock succeeds */ - public void testLock() { + public void testLock() { ReentrantLock rl = new ReentrantLock(); rl.lock(); assertTrue(rl.isLocked()); @@ -89,7 +89,7 @@ public class ReentrantLockTest extends JSR166TestCase { /** * locking an unlocked fair lock succeeds */ - public void testFairLock() { + public void testFairLock() { ReentrantLock rl = new ReentrantLock(true); rl.lock(); assertTrue(rl.isLocked()); @@ -99,7 +99,7 @@ public class ReentrantLockTest extends JSR166TestCase { /** * Unlocking an unlocked lock throws IllegalMonitorStateException */ - public void testUnlock_IllegalMonitorStateException() { + public void testUnlock_IllegalMonitorStateException() { ReentrantLock rl = new ReentrantLock(); try { rl.unlock(); @@ -111,7 +111,7 @@ public class ReentrantLockTest extends JSR166TestCase { /** * tryLock on an unlocked lock succeeds */ - public void testTryLock() { + public void testTryLock() { ReentrantLock rl = new ReentrantLock(); assertTrue(rl.tryLock()); assertTrue(rl.isLocked()); @@ -122,7 +122,7 @@ public class ReentrantLockTest extends JSR166TestCase { /** * hasQueuedThreads reports whether there are waiting threads */ - public void testhasQueuedThreads() { + public void testhasQueuedThreads() { final ReentrantLock lock = new ReentrantLock(); Thread t1 = new Thread(new InterruptedLockRunnable(lock)); Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); @@ -146,12 +146,12 @@ public class ReentrantLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * getQueueLength reports number of waiting threads */ - public void testGetQueueLength() { + public void testGetQueueLength() { final ReentrantLock lock = new ReentrantLock(); Thread t1 = new Thread(new InterruptedLockRunnable(lock)); Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); @@ -175,12 +175,12 @@ public class ReentrantLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * getQueueLength reports number of waiting threads */ - public void testGetQueueLength_fair() { + public void testGetQueueLength_fair() { final ReentrantLock lock = new ReentrantLock(true); Thread t1 = new Thread(new InterruptedLockRunnable(lock)); Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); @@ -204,12 +204,12 @@ public class ReentrantLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * hasQueuedThread(null) throws NPE */ - public void testHasQueuedThreadNPE() { + public void testHasQueuedThreadNPE() { final ReentrantLock sync = new ReentrantLock(); try { sync.hasQueuedThread(null); @@ -221,7 +221,7 @@ public class ReentrantLockTest extends JSR166TestCase { /** * hasQueuedThread reports whether a thread is queued. */ - public void testHasQueuedThread() { + public void testHasQueuedThread() { final ReentrantLock sync = new ReentrantLock(); Thread t1 = new Thread(new InterruptedLockRunnable(sync)); Thread t2 = new Thread(new InterruptibleLockRunnable(sync)); @@ -250,13 +250,13 @@ public class ReentrantLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * getQueuedThreads includes waiting threads */ - public void testGetQueuedThreads() { + public void testGetQueuedThreads() { final PublicReentrantLock lock = new PublicReentrantLock(); Thread t1 = new Thread(new InterruptedLockRunnable(lock)); Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); @@ -283,13 +283,13 @@ public class ReentrantLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * timed tryLock is interruptible. */ - public void testInterruptedException2() { + public void testInterruptedException2() { final ReentrantLock lock = new ReentrantLock(); lock.lock(); Thread t = new Thread(new Runnable() { @@ -312,7 +312,7 @@ public class ReentrantLockTest extends JSR166TestCase { /** * TryLock on a locked lock fails */ - public void testTryLockWhenLocked() { + public void testTryLockWhenLocked() { final ReentrantLock lock = new ReentrantLock(); lock.lock(); Thread t = new Thread(new Runnable() { @@ -327,12 +327,12 @@ public class ReentrantLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * Timed tryLock on a locked lock times out */ - public void testTryLock_Timeout() { + public void testTryLock_Timeout() { final ReentrantLock lock = new ReentrantLock(); lock.lock(); Thread t = new Thread(new Runnable() { @@ -351,8 +351,8 @@ public class ReentrantLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } - + } + /** * getHoldCount returns number of recursive holds */ @@ -367,8 +367,8 @@ public class ReentrantLockTest extends JSR166TestCase { assertEquals(i-1,lock.getHoldCount()); } } - - + + /** * isLocked is true when locked and false when not */ @@ -378,7 +378,7 @@ public class ReentrantLockTest extends JSR166TestCase { assertTrue(lock.isLocked()); lock.unlock(); assertFalse(lock.isLocked()); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { lock.lock(); try { @@ -405,25 +405,27 @@ public class ReentrantLockTest extends JSR166TestCase { /** * lockInterruptibly is interruptible. */ - public void testLockInterruptibly1() { + public void testLockInterruptibly1() { final ReentrantLock lock = new ReentrantLock(); lock.lock(); Thread t = new Thread(new InterruptedLockRunnable(lock)); try { t.start(); + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); + Thread.sleep(SHORT_DELAY_MS); lock.unlock(); t.join(); } catch(Exception e){ unexpectedException(); } - } + } /** * lockInterruptibly succeeds when unlocked, else is interruptible */ public void testLockInterruptibly2() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); try { lock.lockInterruptibly(); } catch(Exception e) { @@ -445,7 +447,7 @@ public class ReentrantLockTest extends JSR166TestCase { * Calling await without holding lock throws IllegalMonitorStateException */ public void testAwait_IllegalMonitor() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); try { c.await(); @@ -462,7 +464,7 @@ public class ReentrantLockTest extends JSR166TestCase { * Calling signal without holding lock throws IllegalMonitorStateException */ public void testSignal_IllegalMonitor() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); try { c.signal(); @@ -479,7 +481,7 @@ public class ReentrantLockTest extends JSR166TestCase { * awaitNanos without a signal times out */ public void testAwaitNanos_Timeout() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); try { lock.lock(); @@ -496,11 +498,11 @@ public class ReentrantLockTest extends JSR166TestCase { * timed await without a signal times out */ public void testAwait_Timeout() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); try { lock.lock(); - assertFalse(c.await(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); + c.await(SHORT_DELAY_MS, TimeUnit.MILLISECONDS); lock.unlock(); } catch (Exception ex) { @@ -512,12 +514,12 @@ public class ReentrantLockTest extends JSR166TestCase { * awaitUntil without a signal times out */ public void testAwaitUntil_Timeout() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); try { lock.lock(); java.util.Date d = new java.util.Date(); - assertFalse(c.awaitUntil(new java.util.Date(d.getTime() + 10))); + c.awaitUntil(new java.util.Date(d.getTime() + 10)); lock.unlock(); } catch (Exception ex) { @@ -529,9 +531,9 @@ public class ReentrantLockTest extends JSR166TestCase { * await returns when signalled */ public void testAwait() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -670,9 +672,9 @@ public class ReentrantLockTest extends JSR166TestCase { * getWaitingThreads throws IAE if not owned */ public void testGetWaitingThreadsIAE() { - final PublicReentrantLock lock = new PublicReentrantLock(); + final PublicReentrantLock lock = new PublicReentrantLock(); final Condition c = (lock.newCondition()); - final PublicReentrantLock lock2 = new PublicReentrantLock(); + final PublicReentrantLock lock2 = new PublicReentrantLock(); try { lock2.getWaitingThreads(c); shouldThrow(); @@ -686,7 +688,7 @@ public class ReentrantLockTest extends JSR166TestCase { * getWaitingThreads throws IMSE if not locked */ public void testGetWaitingThreadsIMSE() { - final PublicReentrantLock lock = new PublicReentrantLock(); + final PublicReentrantLock lock = new PublicReentrantLock(); final Condition c = (lock.newCondition()); try { lock.getWaitingThreads(c); @@ -703,9 +705,9 @@ public class ReentrantLockTest extends JSR166TestCase { * hasWaiters returns true when a thread is waiting, else false */ public void testHasWaiters() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -745,9 +747,9 @@ public class ReentrantLockTest extends JSR166TestCase { * getWaitQueueLength returns number of waiting threads */ public void testGetWaitQueueLength() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); - Thread t1 = new Thread(new Runnable() { + Thread t1 = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -762,7 +764,7 @@ public class ReentrantLockTest extends JSR166TestCase { } }); - Thread t2 = new Thread(new Runnable() { + Thread t2 = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -806,9 +808,9 @@ public class ReentrantLockTest extends JSR166TestCase { * getWaitingThreads returns only and all waiting threads */ public void testGetWaitingThreads() { - final PublicReentrantLock lock = new PublicReentrantLock(); + final PublicReentrantLock lock = new PublicReentrantLock(); final Condition c = lock.newCondition(); - Thread t1 = new Thread(new Runnable() { + Thread t1 = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -822,7 +824,7 @@ public class ReentrantLockTest extends JSR166TestCase { } }); - Thread t2 = new Thread(new Runnable() { + Thread t2 = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -865,34 +867,61 @@ public class ReentrantLockTest extends JSR166TestCase { } } + /** A helper class for uninterruptible wait tests */ + class UninterruptableThread extends Thread { + private ReentrantLock lock; + private Condition c; + + public volatile boolean canAwake = false; + public volatile boolean interrupted = false; + public volatile boolean lockStarted = false; + + public UninterruptableThread(ReentrantLock lock, Condition c) { + this.lock = lock; + this.c = c; + } + + public synchronized void run() { + lock.lock(); + lockStarted = true; + while (!canAwake) { + c.awaitUninterruptibly(); + } + + interrupted = isInterrupted(); + lock.unlock(); + } + } /** * awaitUninterruptibly doesn't abort on interrupt */ public void testAwaitUninterruptibly() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); - Thread t = new Thread(new Runnable() { - public void run() { - lock.lock(); - c.awaitUninterruptibly(); - lock.unlock(); - } - }); + UninterruptableThread thread = new UninterruptableThread(lock, c); try { - t.start(); - Thread.sleep(SHORT_DELAY_MS); - t.interrupt(); + thread.start(); + + while (!thread.lockStarted) { + Thread.sleep(100); + } + lock.lock(); - c.signal(); - lock.unlock(); - assert(t.isInterrupted()); - t.join(SHORT_DELAY_MS); - assertFalse(t.isAlive()); - } - catch (Exception ex) { + try { + thread.interrupt(); + thread.canAwake = true; + c.signal(); + } finally { + lock.unlock(); + } + + thread.join(); + assertTrue(thread.interrupted); + assertFalse(thread.isAlive()); + } catch (Exception ex) { unexpectedException(); } } @@ -901,9 +930,9 @@ public class ReentrantLockTest extends JSR166TestCase { * await is interruptible */ public void testAwait_Interrupt() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -932,9 +961,9 @@ public class ReentrantLockTest extends JSR166TestCase { * awaitNanos is interruptible */ public void testAwaitNanos_Interrupt() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -963,9 +992,9 @@ public class ReentrantLockTest extends JSR166TestCase { * awaitUntil is interruptible */ public void testAwaitUntil_Interrupt() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -995,9 +1024,9 @@ public class ReentrantLockTest extends JSR166TestCase { * signalAll wakes up all threads */ public void testSignalAll() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); - Thread t1 = new Thread(new Runnable() { + Thread t1 = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -1010,7 +1039,7 @@ public class ReentrantLockTest extends JSR166TestCase { } }); - Thread t2 = new Thread(new Runnable() { + Thread t2 = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -1041,6 +1070,61 @@ public class ReentrantLockTest extends JSR166TestCase { } /** + * await after multiple reentrant locking preserves lock count + */ + public void testAwaitLockCount() { + final ReentrantLock lock = new ReentrantLock(); + final Condition c = lock.newCondition(); + Thread t1 = new Thread(new Runnable() { + public void run() { + try { + lock.lock(); + threadAssertEquals(1, lock.getHoldCount()); + c.await(); + threadAssertEquals(1, lock.getHoldCount()); + lock.unlock(); + } + catch(InterruptedException e) { + threadUnexpectedException(); + } + } + }); + + Thread t2 = new Thread(new Runnable() { + public void run() { + try { + lock.lock(); + lock.lock(); + threadAssertEquals(2, lock.getHoldCount()); + c.await(); + threadAssertEquals(2, lock.getHoldCount()); + lock.unlock(); + lock.unlock(); + } + catch(InterruptedException e) { + threadUnexpectedException(); + } + } + }); + + try { + t1.start(); + t2.start(); + Thread.sleep(SHORT_DELAY_MS); + lock.lock(); + c.signalAll(); + lock.unlock(); + t1.join(SHORT_DELAY_MS); + t2.join(SHORT_DELAY_MS); + assertFalse(t1.isAlive()); + assertFalse(t2.isAlive()); + } + catch (Exception ex) { + unexpectedException(); + } + } + + /** * A serialized lock deserializes as unlocked */ public void testSerialization() { diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantReadWriteLockTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantReadWriteLockTest.java index 8925b42..e38165a 100644 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantReadWriteLockTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantReadWriteLockTest.java @@ -16,7 +16,7 @@ import java.util.*; public class ReentrantReadWriteLockTest extends JSR166TestCase { public static void main(String[] args) { - junit.textui.TestRunner.run (suite()); + junit.textui.TestRunner.run (suite()); } public static Test suite() { return new TestSuite(ReentrantReadWriteLockTest.class); @@ -56,18 +56,18 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { */ static class PublicReentrantReadWriteLock extends ReentrantReadWriteLock { PublicReentrantReadWriteLock() { super(); } - public Collection<Thread> getQueuedThreads() { - return super.getQueuedThreads(); + public Collection<Thread> getQueuedThreads() { + return super.getQueuedThreads(); } - public Collection<Thread> getWaitingThreads(Condition c) { - return super.getWaitingThreads(c); + public Collection<Thread> getWaitingThreads(Condition c) { + return super.getWaitingThreads(c); } } /** * Constructor sets given fairness, and is in unlocked state */ - public void testConstructor() { + public void testConstructor() { ReentrantReadWriteLock rl = new ReentrantReadWriteLock(); assertFalse(rl.isFair()); assertFalse(rl.isWriteLocked()); @@ -81,7 +81,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { /** * write-locking and read-locking an unlocked lock succeed */ - public void testLock() { + public void testLock() { ReentrantReadWriteLock rl = new ReentrantReadWriteLock(); rl.writeLock().lock(); assertTrue(rl.isWriteLocked()); @@ -105,7 +105,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { /** * locking an unlocked fair lock succeeds */ - public void testFairLock() { + public void testFairLock() { ReentrantReadWriteLock rl = new ReentrantReadWriteLock(true); rl.writeLock().lock(); assertTrue(rl.isWriteLocked()); @@ -128,23 +128,23 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { /** * getWriteHoldCount returns number of recursive holds */ - public void testGetHoldCount() { - ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); - for(int i = 1; i <= SIZE; i++) { - lock.writeLock().lock(); - assertEquals(i,lock.getWriteHoldCount()); - } - for(int i = SIZE; i > 0; i--) { - lock.writeLock().unlock(); - assertEquals(i-1,lock.getWriteHoldCount()); - } + public void testGetWriteHoldCount() { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + for(int i = 1; i <= SIZE; i++) { + lock.writeLock().lock(); + assertEquals(i,lock.getWriteHoldCount()); + } + for(int i = SIZE; i > 0; i--) { + lock.writeLock().unlock(); + assertEquals(i-1,lock.getWriteHoldCount()); + } } - + /** * write-unlocking an unlocked lock throws IllegalMonitorStateException */ - public void testUnlock_IllegalMonitorStateException() { + public void testUnlock_IllegalMonitorStateException() { ReentrantReadWriteLock rl = new ReentrantReadWriteLock(); try { rl.writeLock().unlock(); @@ -156,7 +156,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { /** * write-lockInterruptibly is interruptible */ - public void testWriteLockInterruptibly_Interrupted() { + public void testWriteLockInterruptibly_Interrupted() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); Thread t = new Thread(new Runnable() { public void run() { @@ -171,18 +171,20 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { try { lock.writeLock().lock(); t.start(); + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); + Thread.sleep(SHORT_DELAY_MS); lock.writeLock().unlock(); t.join(); } catch(Exception e){ unexpectedException(); } - } + } /** * timed write-tryLock is interruptible */ - public void testWriteTryLock_Interrupted() { + public void testWriteTryLock_Interrupted() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); Thread t = new Thread(new Runnable() { @@ -205,7 +207,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { /** * read-lockInterruptibly is interruptible */ - public void testReadLockInterruptibly_Interrupted() { + public void testReadLockInterruptibly_Interrupted() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); Thread t = new Thread(new Runnable() { @@ -217,18 +219,20 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { }); try { t.start(); + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); + Thread.sleep(SHORT_DELAY_MS); lock.writeLock().unlock(); t.join(); } catch(Exception e){ unexpectedException(); } - } + } /** * timed read-tryLock is interruptible */ - public void testReadTryLock_Interrupted() { + public void testReadTryLock_Interrupted() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); Thread t = new Thread(new Runnable() { @@ -248,11 +252,11 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } } - + /** * write-tryLock fails if locked */ - public void testWriteTryLockWhenLocked() { + public void testWriteTryLockWhenLocked() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); Thread t = new Thread(new Runnable() { @@ -267,12 +271,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * read-tryLock fails if locked */ - public void testReadTryLockWhenLocked() { + public void testReadTryLockWhenLocked() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); Thread t = new Thread(new Runnable() { @@ -287,12 +291,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * Multiple threads can hold a read lock when not write-locked */ - public void testMultipleReadLocks() { + public void testMultipleReadLocks() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.readLock().lock(); Thread t = new Thread(new Runnable() { @@ -308,12 +312,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * A writelock succeeds after reading threads unlock */ - public void testWriteAfterMultipleReadLocks() { + public void testWriteAfterMultipleReadLocks() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.readLock().lock(); Thread t1 = new Thread(new Runnable() { @@ -338,16 +342,16 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { t2.join(MEDIUM_DELAY_MS); assertTrue(!t1.isAlive()); assertTrue(!t2.isAlive()); - + } catch(Exception e){ unexpectedException(); } - } + } /** * Readlocks succeed after a writing thread unlocks */ - public void testReadAfterWriteLock() { + public void testReadAfterWriteLock() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); Thread t1 = new Thread(new Runnable() { @@ -372,17 +376,280 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { t2.join(MEDIUM_DELAY_MS); assertTrue(!t1.isAlive()); assertTrue(!t2.isAlive()); - + + } catch(Exception e){ + unexpectedException(); + } + } + + /** + * Read trylock succeeds if write locked by current thread + */ + public void testReadHoldingWriteLock() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + lock.writeLock().lock(); + assertTrue(lock.readLock().tryLock()); + lock.readLock().unlock(); + lock.writeLock().unlock(); + } + + /** + * Read lock succeeds if write locked by current thread even if + * other threads are waiting for readlock + */ + public void testReadHoldingWriteLock2() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + lock.writeLock().lock(); + Thread t1 = new Thread(new Runnable() { + public void run() { + lock.readLock().lock(); + lock.readLock().unlock(); + } + }); + Thread t2 = new Thread(new Runnable() { + public void run() { + lock.readLock().lock(); + lock.readLock().unlock(); + } + }); + + try { + t1.start(); + t2.start(); + lock.readLock().lock(); + lock.readLock().unlock(); + Thread.sleep(SHORT_DELAY_MS); + lock.readLock().lock(); + lock.readLock().unlock(); + lock.writeLock().unlock(); + t1.join(MEDIUM_DELAY_MS); + t2.join(MEDIUM_DELAY_MS); + assertTrue(!t1.isAlive()); + assertTrue(!t2.isAlive()); + + } catch(Exception e){ + unexpectedException(); + } + } + + /** + * Read lock succeeds if write locked by current thread even if + * other threads are waiting for writelock + */ + public void testReadHoldingWriteLock3() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + lock.writeLock().lock(); + Thread t1 = new Thread(new Runnable() { + public void run() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + } + }); + Thread t2 = new Thread(new Runnable() { + public void run() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + } + }); + + try { + t1.start(); + t2.start(); + lock.readLock().lock(); + lock.readLock().unlock(); + Thread.sleep(SHORT_DELAY_MS); + lock.readLock().lock(); + lock.readLock().unlock(); + lock.writeLock().unlock(); + t1.join(MEDIUM_DELAY_MS); + t2.join(MEDIUM_DELAY_MS); + assertTrue(!t1.isAlive()); + assertTrue(!t2.isAlive()); + + } catch(Exception e){ + unexpectedException(); + } + } + + + /** + * Write lock succeeds if write locked by current thread even if + * other threads are waiting for writelock + */ + public void testWriteHoldingWriteLock4() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + lock.writeLock().lock(); + Thread t1 = new Thread(new Runnable() { + public void run() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + } + }); + Thread t2 = new Thread(new Runnable() { + public void run() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + } + }); + + try { + t1.start(); + t2.start(); + lock.writeLock().lock(); + lock.writeLock().unlock(); + Thread.sleep(SHORT_DELAY_MS); + lock.writeLock().lock(); + lock.writeLock().unlock(); + lock.writeLock().unlock(); + t1.join(MEDIUM_DELAY_MS); + t2.join(MEDIUM_DELAY_MS); + assertTrue(!t1.isAlive()); + assertTrue(!t2.isAlive()); + + } catch(Exception e){ + unexpectedException(); + } + } + + + /** + * Fair Read trylock succeeds if write locked by current thread + */ + public void testReadHoldingWriteLockFair() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + lock.writeLock().lock(); + assertTrue(lock.readLock().tryLock()); + lock.readLock().unlock(); + lock.writeLock().unlock(); + } + + /** + * Fair Read lock succeeds if write locked by current thread even if + * other threads are waiting for readlock + */ + public void testReadHoldingWriteLockFair2() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + lock.writeLock().lock(); + Thread t1 = new Thread(new Runnable() { + public void run() { + lock.readLock().lock(); + lock.readLock().unlock(); + } + }); + Thread t2 = new Thread(new Runnable() { + public void run() { + lock.readLock().lock(); + lock.readLock().unlock(); + } + }); + + try { + t1.start(); + t2.start(); + lock.readLock().lock(); + lock.readLock().unlock(); + Thread.sleep(SHORT_DELAY_MS); + lock.readLock().lock(); + lock.readLock().unlock(); + lock.writeLock().unlock(); + t1.join(MEDIUM_DELAY_MS); + t2.join(MEDIUM_DELAY_MS); + assertTrue(!t1.isAlive()); + assertTrue(!t2.isAlive()); + + } catch(Exception e){ + unexpectedException(); + } + } + + + /** + * Fair Read lock succeeds if write locked by current thread even if + * other threads are waiting for writelock + */ + public void testReadHoldingWriteLockFair3() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + lock.writeLock().lock(); + Thread t1 = new Thread(new Runnable() { + public void run() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + } + }); + Thread t2 = new Thread(new Runnable() { + public void run() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + } + }); + + try { + t1.start(); + t2.start(); + lock.readLock().lock(); + lock.readLock().unlock(); + Thread.sleep(SHORT_DELAY_MS); + lock.readLock().lock(); + lock.readLock().unlock(); + lock.writeLock().unlock(); + t1.join(MEDIUM_DELAY_MS); + t2.join(MEDIUM_DELAY_MS); + assertTrue(!t1.isAlive()); + assertTrue(!t2.isAlive()); + } catch(Exception e){ unexpectedException(); } - } + } + + + /** + * Fair Write lock succeeds if write locked by current thread even if + * other threads are waiting for writelock + */ + public void testWriteHoldingWriteLockFair4() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + lock.writeLock().lock(); + Thread t1 = new Thread(new Runnable() { + public void run() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + } + }); + Thread t2 = new Thread(new Runnable() { + public void run() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + } + }); + + try { + t1.start(); + t2.start(); + Thread.sleep(SHORT_DELAY_MS); + assertTrue(lock.isWriteLockedByCurrentThread()); + assertTrue(lock.getWriteHoldCount() == 1); + lock.writeLock().lock(); + assertTrue(lock.getWriteHoldCount() == 2); + lock.writeLock().unlock(); + lock.writeLock().lock(); + lock.writeLock().unlock(); + lock.writeLock().unlock(); + t1.join(MEDIUM_DELAY_MS); + t2.join(MEDIUM_DELAY_MS); + assertTrue(!t1.isAlive()); + assertTrue(!t2.isAlive()); + + } catch(Exception e){ + unexpectedException(); + } + } /** * Read tryLock succeeds if readlocked but not writelocked */ - public void testTryLockWhenReadLocked() { + public void testTryLockWhenReadLocked() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.readLock().lock(); Thread t = new Thread(new Runnable() { @@ -398,14 +665,14 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } + - /** * write tryLock fails when readlocked */ - public void testWriteTryLockWhenReadLocked() { + public void testWriteTryLockWhenReadLocked() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.readLock().lock(); Thread t = new Thread(new Runnable() { @@ -420,14 +687,58 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } + + + /** + * Fair Read tryLock succeeds if readlocked but not writelocked + */ + public void testTryLockWhenReadLockedFair() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + lock.readLock().lock(); + Thread t = new Thread(new Runnable() { + public void run() { + threadAssertTrue(lock.readLock().tryLock()); + lock.readLock().unlock(); + } + }); + try { + t.start(); + t.join(); + lock.readLock().unlock(); + } catch(Exception e){ + unexpectedException(); + } + } + + + + /** + * Fair write tryLock fails when readlocked + */ + public void testWriteTryLockWhenReadLockedFair() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + lock.readLock().lock(); + Thread t = new Thread(new Runnable() { + public void run() { + threadAssertFalse(lock.writeLock().tryLock()); + } + }); + try { + t.start(); + t.join(); + lock.readLock().unlock(); + } catch(Exception e){ + unexpectedException(); + } + } + - /** * write timed tryLock times out if locked */ - public void testWriteTryLock_Timeout() { + public void testWriteTryLock_Timeout() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); Thread t = new Thread(new Runnable() { @@ -446,12 +757,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * read timed tryLock times out if write-locked */ - public void testReadTryLock_Timeout() { + public void testReadTryLock_Timeout() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); Thread t = new Thread(new Runnable() { @@ -470,7 +781,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** @@ -483,7 +794,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e) { unexpectedException(); } - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.writeLock().lockInterruptibly(); @@ -495,7 +806,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { }); try { t.start(); + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); + Thread.sleep(SHORT_DELAY_MS); t.join(); lock.writeLock().unlock(); } catch(Exception e){ @@ -513,7 +826,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e) { unexpectedException(); } - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.readLock().lockInterruptibly(); @@ -525,6 +838,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { }); try { t.start(); + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); t.join(); lock.writeLock().unlock(); @@ -537,7 +851,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * Calling await without holding lock throws IllegalMonitorStateException */ public void testAwait_IllegalMonitor() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); try { c.await(); @@ -554,7 +868,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * Calling signal without holding lock throws IllegalMonitorStateException */ public void testSignal_IllegalMonitor() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); try { c.signal(); @@ -571,7 +885,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * awaitNanos without a signal times out */ public void testAwaitNanos_Timeout() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); try { lock.writeLock().lock(); @@ -589,11 +903,10 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * timed await without a signal times out */ public void testAwait_Timeout() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); try { lock.writeLock().lock(); - assertFalse(c.await(10, TimeUnit.MILLISECONDS)); lock.writeLock().unlock(); } catch (Exception ex) { @@ -605,12 +918,11 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * awaitUntil without a signal times out */ public void testAwaitUntil_Timeout() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); try { lock.writeLock().lock(); java.util.Date d = new java.util.Date(); - assertFalse(c.awaitUntil(new java.util.Date(d.getTime() + 10))); lock.writeLock().unlock(); } catch (Exception ex) { @@ -622,9 +934,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * await returns when signalled */ public void testAwait() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -651,32 +963,61 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } } + /** A helper class for uninterruptible wait tests */ + class UninterruptableThread extends Thread { + private Lock lock; + private Condition c; + + public volatile boolean canAwake = false; + public volatile boolean interrupted = false; + public volatile boolean lockStarted = false; + + public UninterruptableThread(Lock lock, Condition c) { + this.lock = lock; + this.c = c; + } + + public synchronized void run() { + lock.lock(); + lockStarted = true; + + while (!canAwake) { + c.awaitUninterruptibly(); + } + + interrupted = isInterrupted(); + lock.unlock(); + } + } + /** * awaitUninterruptibly doesn't abort on interrupt */ public void testAwaitUninterruptibly() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); - Thread t = new Thread(new Runnable() { - public void run() { - lock.writeLock().lock(); - c.awaitUninterruptibly(); - lock.writeLock().unlock(); - } - }); + UninterruptableThread thread = new UninterruptableThread(lock.writeLock(), c); try { - t.start(); - Thread.sleep(SHORT_DELAY_MS); - t.interrupt(); + thread.start(); + + while (!thread.lockStarted) { + Thread.sleep(100); + } + lock.writeLock().lock(); - c.signal(); - lock.writeLock().unlock(); - assert(t.isInterrupted()); - t.join(SHORT_DELAY_MS); - assertFalse(t.isAlive()); - } - catch (Exception ex) { + try { + thread.interrupt(); + thread.canAwake = true; + c.signal(); + } finally { + lock.writeLock().unlock(); + } + + thread.join(); + assertTrue(thread.interrupted); + assertFalse(thread.isAlive()); + } catch (Exception ex) { unexpectedException(); } } @@ -685,9 +1026,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * await is interruptible */ public void testAwait_Interrupt() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -716,9 +1057,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * awaitNanos is interruptible */ public void testAwaitNanos_Interrupt() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -747,9 +1088,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * awaitUntil is interruptible */ public void testAwaitUntil_Interrupt() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -779,9 +1120,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * signalAll wakes up all threads */ public void testSignalAll() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); - Thread t1 = new Thread(new Runnable() { + Thread t1 = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -794,7 +1135,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } }); - Thread t2 = new Thread(new Runnable() { + Thread t2 = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -852,7 +1193,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { /** * hasQueuedThreads reports whether there are waiting threads */ - public void testhasQueuedThreads() { + public void testhasQueuedThreads() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); Thread t1 = new Thread(new InterruptedLockRunnable(lock)); Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); @@ -876,12 +1217,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * hasQueuedThread(null) throws NPE */ - public void testHasQueuedThreadNPE() { + public void testHasQueuedThreadNPE() { final ReentrantReadWriteLock sync = new ReentrantReadWriteLock(); try { sync.hasQueuedThread(null); @@ -893,7 +1234,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { /** * hasQueuedThread reports whether a thread is queued. */ - public void testHasQueuedThread() { + public void testHasQueuedThread() { final ReentrantReadWriteLock sync = new ReentrantReadWriteLock(); Thread t1 = new Thread(new InterruptedLockRunnable(sync)); Thread t2 = new Thread(new InterruptibleLockRunnable(sync)); @@ -922,13 +1263,13 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * getQueueLength reports number of waiting threads */ - public void testGetQueueLength() { + public void testGetQueueLength() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); Thread t1 = new Thread(new InterruptedLockRunnable(lock)); Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); @@ -952,12 +1293,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * getQueuedThreads includes waiting threads */ - public void testGetQueuedThreads() { + public void testGetQueuedThreads() { final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(); Thread t1 = new Thread(new InterruptedLockRunnable(lock)); Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); @@ -984,7 +1325,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * hasWaiters throws NPE if null @@ -1097,9 +1438,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * getWaitingThreads throws IAE if not owned */ public void testGetWaitingThreadsIAE() { - final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(); + final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(); final Condition c = (lock.writeLock().newCondition()); - final PublicReentrantReadWriteLock lock2 = new PublicReentrantReadWriteLock(); + final PublicReentrantReadWriteLock lock2 = new PublicReentrantReadWriteLock(); try { lock2.getWaitingThreads(c); shouldThrow(); @@ -1113,7 +1454,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * getWaitingThreads throws IMSE if not locked */ public void testGetWaitingThreadsIMSE() { - final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(); + final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(); final Condition c = (lock.writeLock().newCondition()); try { lock.getWaitingThreads(c); @@ -1131,7 +1472,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { public void testHasWaiters() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = (lock.writeLock().newCondition()); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -1173,7 +1514,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { public void testGetWaitQueueLength() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = (lock.writeLock().newCondition()); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -1214,9 +1555,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * getWaitingThreads returns only and all waiting threads */ public void testGetWaitingThreads() { - final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(); + final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); - Thread t1 = new Thread(new Runnable() { + Thread t1 = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -1230,7 +1571,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } }); - Thread t2 = new Thread(new Runnable() { + Thread t2 = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/SynchronousQueueTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/SynchronousQueueTest.java index 19bdede..debce5d 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/SynchronousQueueTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/SynchronousQueueTest.java @@ -757,6 +757,7 @@ public class SynchronousQueueTest extends JSR166TestCase { while (!q.isEmpty()) assertEquals(q.remove(), r.remove()); } catch(Exception e){ + e.printStackTrace(); unexpectedException(); } } diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/ThreadPoolExecutorTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/ThreadPoolExecutorTest.java index ca4ba78..4f7cc46 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/ThreadPoolExecutorTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/ThreadPoolExecutorTest.java @@ -9,6 +9,7 @@ package tests.api.java.util.concurrent; import java.util.concurrent.*; +import java.util.concurrent.atomic.*; import junit.framework.*; import java.util.*; @@ -38,6 +39,15 @@ public class ThreadPoolExecutorTest extends JSR166TestCase { } } + static class FailingThreadFactory implements ThreadFactory{ + int calls = 0; + public Thread newThread(Runnable r){ + if (++calls > 1) return null; + return new Thread(r); + } + } + + /** * execute successfully executes a runnable */ @@ -342,6 +352,9 @@ public class ThreadPoolExecutorTest extends JSR166TestCase { assertSame(q, wq); assertFalse(wq.contains(tasks[0])); assertTrue(wq.contains(tasks[4])); + for (int i = 1; i < 5; ++i) + tasks[i].cancel(true); + p1.shutdownNow(); } catch(Exception e) { unexpectedException(); } finally { @@ -1500,5 +1513,58 @@ public class ThreadPoolExecutorTest extends JSR166TestCase { } } + /** + * Execution continues if there is at least one thread even if + * thread factory fails to create more + */ + public void testFailingThreadFactory() { + ExecutorService e = new ThreadPoolExecutor(100, 100, LONG_DELAY_MS, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new FailingThreadFactory()); + try { + ArrayList<Callable<String>> l = new ArrayList<Callable<String>>(); + for (int k = 0; k < 100; ++k) { + e.execute(new NoOpRunnable()); + } + Thread.sleep(LONG_DELAY_MS); + } catch(Exception ex) { + unexpectedException(); + } finally { + joinPool(e); + } + } + /** + * execute allows the same task to be submitted multiple times, even + * if rejected + */ + public void testRejectedRecycledTask() { + final int nTasks = 1000; + final AtomicInteger nRun = new AtomicInteger(0); + final Runnable recycledTask = new Runnable() { + public void run() { + nRun.getAndIncrement(); + } }; + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 30, 60, TimeUnit.SECONDS, + new ArrayBlockingQueue(30)); + try { + for (int i = 0; i < nTasks; ++i) { + for (;;) { + try { + p.execute(recycledTask); + break; + } + catch (RejectedExecutionException ignore) { + } + } + } + Thread.sleep(5000); // enough time to run all tasks + assertEquals(nRun.get(), nTasks); + } catch(Exception ex) { + ex.printStackTrace(); + unexpectedException(); + } finally { + p.shutdown(); + } + } + } diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/TimeUnitTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/TimeUnitTest.java index 1a9a04a..54fdc69 100755 --- a/concurrent/src/test/java/tests/api/java/util/concurrent/TimeUnitTest.java +++ b/concurrent/src/test/java/tests/api/java/util/concurrent/TimeUnitTest.java @@ -21,54 +21,60 @@ public class TimeUnitTest extends JSR166TestCase { return new TestSuite(TimeUnitTest.class); } + // (loops to 88888 check increments at all time divisions.) + /** - * convert correctly converts sample values across the four units + * convert correctly converts sample values across the units */ public void testConvert() { - for (long t = 0; t < 10; ++t) { - assertEquals(t, + for (long t = 0; t < 88888; ++t) { + assertEquals(t, TimeUnit.SECONDS.convert(t, TimeUnit.SECONDS)); assertEquals(t, - TimeUnit.SECONDS.convert(1000 * t, + TimeUnit.SECONDS.convert(1000L*t, TimeUnit.MILLISECONDS)); assertEquals(t, - TimeUnit.SECONDS.convert(1000000 * t, + TimeUnit.SECONDS.convert(1000000L*t, TimeUnit.MICROSECONDS)); assertEquals(t, - TimeUnit.SECONDS.convert(1000000000 * t, + TimeUnit.SECONDS.convert(1000000000L*t, TimeUnit.NANOSECONDS)); - assertEquals(1000 * t, + + + assertEquals(1000L*t, TimeUnit.MILLISECONDS.convert(t, TimeUnit.SECONDS)); assertEquals(t, TimeUnit.MILLISECONDS.convert(t, TimeUnit.MILLISECONDS)); assertEquals(t, - TimeUnit.MILLISECONDS.convert(1000 * t, + TimeUnit.MILLISECONDS.convert(1000L*t, TimeUnit.MICROSECONDS)); assertEquals(t, - TimeUnit.MILLISECONDS.convert(1000000 * t, + TimeUnit.MILLISECONDS.convert(1000000L*t, TimeUnit.NANOSECONDS)); - assertEquals(1000000 * t, + + assertEquals(1000000L*t, TimeUnit.MICROSECONDS.convert(t, TimeUnit.SECONDS)); - assertEquals(1000 * t, + assertEquals(1000L*t, TimeUnit.MICROSECONDS.convert(t, TimeUnit.MILLISECONDS)); assertEquals(t, TimeUnit.MICROSECONDS.convert(t, TimeUnit.MICROSECONDS)); assertEquals(t, - TimeUnit.MICROSECONDS.convert(1000 * t, + TimeUnit.MICROSECONDS.convert(1000L*t, TimeUnit.NANOSECONDS)); - assertEquals(1000000000 * t, + + assertEquals(1000000000L*t, TimeUnit.NANOSECONDS.convert(t, TimeUnit.SECONDS)); - assertEquals(1000000 * t, + assertEquals(1000000L*t, TimeUnit.NANOSECONDS.convert(t, TimeUnit.MILLISECONDS)); - assertEquals(1000 * t, + assertEquals(1000L*t, TimeUnit.NANOSECONDS.convert(t, TimeUnit.MICROSECONDS)); assertEquals(t, @@ -82,13 +88,12 @@ public class TimeUnitTest extends JSR166TestCase { * nanoseconds */ public void testToNanos() { - for (long t = 0; t < 10; ++t) { - assertEquals(1000000000 * t, + for (long t = 0; t < 88888; ++t) { + assertEquals(1000000000L*t, TimeUnit.SECONDS.toNanos(t)); - - assertEquals(1000000 * t, + assertEquals(1000000L*t, TimeUnit.MILLISECONDS.toNanos(t)); - assertEquals(1000 * t, + assertEquals(1000L*t, TimeUnit.MICROSECONDS.toNanos(t)); assertEquals(t, TimeUnit.NANOSECONDS.toNanos(t)); @@ -100,16 +105,15 @@ public class TimeUnitTest extends JSR166TestCase { * microseconds */ public void testToMicros() { - for (long t = 0; t < 10; ++t) { - assertEquals(1000000 * t, + for (long t = 0; t < 88888; ++t) { + assertEquals(1000000L*t, TimeUnit.SECONDS.toMicros(t)); - - assertEquals(1000 * t, + assertEquals(1000L*t, TimeUnit.MILLISECONDS.toMicros(t)); assertEquals(t, TimeUnit.MICROSECONDS.toMicros(t)); assertEquals(t, - TimeUnit.NANOSECONDS.toMicros(t * 1000)); + TimeUnit.NANOSECONDS.toMicros(t*1000L)); } } @@ -118,16 +122,15 @@ public class TimeUnitTest extends JSR166TestCase { * milliseconds */ public void testToMillis() { - for (long t = 0; t < 10; ++t) { - assertEquals(1000 * t, + for (long t = 0; t < 88888; ++t) { + assertEquals(1000L*t, TimeUnit.SECONDS.toMillis(t)); - assertEquals(t, TimeUnit.MILLISECONDS.toMillis(t)); assertEquals(t, - TimeUnit.MICROSECONDS.toMillis(t * 1000)); + TimeUnit.MICROSECONDS.toMillis(t*1000L)); assertEquals(t, - TimeUnit.NANOSECONDS.toMillis(t * 1000000)); + TimeUnit.NANOSECONDS.toMillis(t*1000000L)); } } @@ -136,20 +139,18 @@ public class TimeUnitTest extends JSR166TestCase { * seconds */ public void testToSeconds() { - for (long t = 0; t < 10; ++t) { - assertEquals(t, + for (long t = 0; t < 88888; ++t) { + assertEquals(t, TimeUnit.SECONDS.toSeconds(t)); - assertEquals(t, - TimeUnit.MILLISECONDS.toSeconds(t * 1000)); + TimeUnit.MILLISECONDS.toSeconds(t*1000L)); assertEquals(t, - TimeUnit.MICROSECONDS.toSeconds(t * 1000000)); + TimeUnit.MICROSECONDS.toSeconds(t*1000000L)); assertEquals(t, - TimeUnit.NANOSECONDS.toSeconds(t * 1000000000)); + TimeUnit.NANOSECONDS.toSeconds(t*1000000000L)); } } - /** * convert saturates positive too-large values to Long.MAX_VALUE * and negative to LONG.MIN_VALUE @@ -161,6 +162,7 @@ public class TimeUnitTest extends JSR166TestCase { assertEquals(Long.MIN_VALUE, TimeUnit.NANOSECONDS.convert(-Long.MAX_VALUE / 4, TimeUnit.SECONDS)); + } /** diff --git a/dalvik/src/main/java/dalvik/system/VMStack.java b/dalvik/src/main/java/dalvik/system/VMStack.java index 9330c68..88d9bcd 100644 --- a/dalvik/src/main/java/dalvik/system/VMStack.java +++ b/dalvik/src/main/java/dalvik/system/VMStack.java @@ -40,6 +40,13 @@ public final class VMStack { native public static ClassLoader getCallingClassLoader2(); /** + * Returns the class of the caller's caller's caller. + * + * @return the requested class, or {@code null}. + */ + native public static Class<?> getStackClass2(); + + /** * Creates an array of classes from the methods at the top of the stack. * We continue until we reach the bottom of the stack or exceed the * specified maximum depth. If stopAtPrivileged is set, the last |