diff options
author | Jesse Wilson <jessewilson@google.com> | 2010-04-07 15:07:54 -0700 |
---|---|---|
committer | Jesse Wilson <jessewilson@google.com> | 2010-04-09 12:15:23 -0700 |
commit | 6232a5efed0ea103e35aec73206e5e03a8b82e0c (patch) | |
tree | 1063168102abba77194c174b27f997f8508b104f /concurrent/src/main/java | |
parent | 5ce230557f6d172654868465c6ef9f99564701a8 (diff) | |
download | libcore-6232a5efed0ea103e35aec73206e5e03a8b82e0c.zip libcore-6232a5efed0ea103e35aec73206e5e03a8b82e0c.tar.gz libcore-6232a5efed0ea103e35aec73206e5e03a8b82e0c.tar.bz2 |
Latest java.util.concurrent from the JSR 166 project.
Code was downloaded from CVS on 2010-april-7 at 10:00pst
http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/
The new interfaces and ArrayDeque class are all from Harmony.
Diffstat (limited to 'concurrent/src/main/java')
47 files changed, 10236 insertions, 814 deletions
diff --git a/concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java b/concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java index f7ed15c..a7c8fe5 100644 --- a/concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java +++ b/concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java @@ -10,26 +10,74 @@ import java.util.*; /** * 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, + * <tt>invokeAny</tt> and <tt>invokeAll</tt> methods using a + * {@link RunnableFuture} returned by <tt>newTaskFor</tt>, which defaults + * to the {@link FutureTask} class provided in this package. For example, * the implementation of <tt>submit(Runnable)</tt> creates an - * associated <tt>FutureTask</tt> that is executed and - * returned. Subclasses overriding these methods to use different - * {@link Future} implementations should do so consistently for each - * of these methods. + * associated <tt>RunnableFuture</tt> that is executed and + * returned. Subclasses may override the <tt>newTaskFor</tt> methods + * to return <tt>RunnableFuture</tt> implementations other than + * <tt>FutureTask</tt>. * + * <p> <b>Extension example</b>. Here is a sketch of a class + * that customizes {@link ThreadPoolExecutor} to use + * a <tt>CustomTask</tt> class instead of the default <tt>FutureTask</tt>: + * <pre> + * public class CustomThreadPoolExecutor extends ThreadPoolExecutor { + * + * static class CustomTask<V> implements RunnableFuture<V> {...} + * + * protected <V> RunnableFuture<V> newTaskFor(Callable<V> c) { + * return new CustomTask<V>(c); + * } + * protected <V> RunnableFuture<V> newTaskFor(Runnable r, V v) { + * return new CustomTask<V>(r, v); + * } + * // ... add constructors, etc. + * } + * </pre> * @since 1.5 * @author Doug Lea */ public abstract class AbstractExecutorService implements ExecutorService { /** + * Returns a <tt>RunnableFuture</tt> for the given runnable and default + * value. + * + * @param runnable the runnable task being wrapped + * @param value the default value for the returned future + * @return a <tt>RunnableFuture</tt> which when run will run the + * underlying runnable and which, as a <tt>Future</tt>, will yield + * the given value as its result and provide for cancellation of + * the underlying task. + * @since 1.6 + */ + protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { + return new FutureTask<T>(runnable, value); + } + + /** + * Returns a <tt>RunnableFuture</tt> for the given callable task. + * + * @param callable the callable task being wrapped + * @return a <tt>RunnableFuture</tt> which when run will call the + * underlying callable and which, as a <tt>Future</tt>, will yield + * the callable's result as its result and provide for + * cancellation of the underlying task. + * @since 1.6 + */ + protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { + return new FutureTask<T>(callable); + } + + /** * @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); + RunnableFuture<Object> ftask = newTaskFor(task, null); execute(ftask); return ftask; } @@ -40,7 +88,7 @@ public abstract class AbstractExecutorService implements ExecutorService { */ public <T> Future<T> submit(Runnable task, T result) { if (task == null) throw new NullPointerException(); - FutureTask<T> ftask = new FutureTask<T>(task, result); + RunnableFuture<T> ftask = newTaskFor(task, result); execute(ftask); return ftask; } @@ -51,7 +99,7 @@ public abstract class AbstractExecutorService implements ExecutorService { */ public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); - FutureTask<T> ftask = new FutureTask<T>(task); + RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; } @@ -59,7 +107,7 @@ public abstract class AbstractExecutorService implements ExecutorService { /** * the main mechanics of invokeAny. */ - private <T> T doInvokeAny(Collection<Callable<T>> tasks, + private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks, boolean timed, long nanos) throws InterruptedException, ExecutionException, TimeoutException { if (tasks == null) @@ -82,7 +130,7 @@ public abstract class AbstractExecutorService implements ExecutorService { // result, we can throw the last exception we got. ExecutionException ee = null; long lastTime = (timed)? System.nanoTime() : 0; - Iterator<Callable<T>> it = tasks.iterator(); + Iterator<? extends Callable<T>> it = tasks.iterator(); // Start one task for sure; the rest incrementally futures.add(ecs.submit(it.next())); @@ -134,7 +182,7 @@ public abstract class AbstractExecutorService implements ExecutorService { } } - public <T> T invokeAny(Collection<Callable<T>> tasks) + public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException { try { return doInvokeAny(tasks, false, 0); @@ -144,13 +192,13 @@ public abstract class AbstractExecutorService implements ExecutorService { } } - public <T> T invokeAny(Collection<Callable<T>> tasks, + public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return doInvokeAny(tasks, true, unit.toNanos(timeout)); } - public <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks) + public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { if (tasks == null) throw new NullPointerException(); @@ -158,7 +206,7 @@ public abstract class AbstractExecutorService implements ExecutorService { boolean done = false; try { for (Callable<T> t : tasks) { - FutureTask<T> f = new FutureTask<T>(t); + RunnableFuture<T> f = newTaskFor(t); futures.add(f); execute(f); } @@ -180,7 +228,7 @@ public abstract class AbstractExecutorService implements ExecutorService { } } - public <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks, + public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException { if (tasks == null || unit == null) @@ -190,7 +238,7 @@ public abstract class AbstractExecutorService implements ExecutorService { boolean done = false; try { for (Callable<T> t : tasks) - futures.add(new FutureTask<T>(t)); + futures.add(newTaskFor(t)); long lastTime = System.nanoTime(); diff --git a/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java b/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java index 0082f07..1c6f613 100644 --- a/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java +++ b/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java @@ -189,8 +189,8 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> if (capacity < c.size()) throw new IllegalArgumentException(); - for (Iterator<? extends E> it = c.iterator(); it.hasNext();) - add(it.next()); + for (E e : c) + add(e); } /** diff --git a/concurrent/src/main/java/java/util/concurrent/BlockingDeque.java b/concurrent/src/main/java/java/util/concurrent/BlockingDeque.java new file mode 100644 index 0000000..d77a965 --- /dev/null +++ b/concurrent/src/main/java/java/util/concurrent/BlockingDeque.java @@ -0,0 +1,613 @@ +/* + * 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; +import java.util.*; + +/** + * A {@link Deque} that additionally supports blocking operations that wait + * for the deque to become non-empty when retrieving an element, and wait for + * space to become available in the deque when storing an element. + * + * <p><tt>BlockingDeque</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 ALIGN=CENTER COLSPAN = 5> <b>First Element (Head)</b></td> + * </tr> + * <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 #addFirst addFirst(e)}</td> + * <td>{@link #offerFirst(Object) offerFirst(e)}</td> + * <td>{@link #putFirst putFirst(e)}</td> + * <td>{@link #offerFirst(Object, long, TimeUnit) offerFirst(e, time, unit)}</td> + * </tr> + * <tr> + * <td><b>Remove</b></td> + * <td>{@link #removeFirst removeFirst()}</td> + * <td>{@link #pollFirst pollFirst()}</td> + * <td>{@link #takeFirst takeFirst()}</td> + * <td>{@link #pollFirst(long, TimeUnit) pollFirst(time, unit)}</td> + * </tr> + * <tr> + * <td><b>Examine</b></td> + * <td>{@link #getFirst getFirst()}</td> + * <td>{@link #peekFirst peekFirst()}</td> + * <td><em>not applicable</em></td> + * <td><em>not applicable</em></td> + * </tr> + * <tr> + * <td ALIGN=CENTER COLSPAN = 5> <b>Last Element (Tail)</b></td> + * </tr> + * <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 #addLast addLast(e)}</td> + * <td>{@link #offerLast(Object) offerLast(e)}</td> + * <td>{@link #putLast putLast(e)}</td> + * <td>{@link #offerLast(Object, long, TimeUnit) offerLast(e, time, unit)}</td> + * </tr> + * <tr> + * <td><b>Remove</b></td> + * <td>{@link #removeLast() removeLast()}</td> + * <td>{@link #pollLast() pollLast()}</td> + * <td>{@link #takeLast takeLast()}</td> + * <td>{@link #pollLast(long, TimeUnit) pollLast(time, unit)}</td> + * </tr> + * <tr> + * <td><b>Examine</b></td> + * <td>{@link #getLast getLast()}</td> + * <td>{@link #peekLast peekLast()}</td> + * <td><em>not applicable</em></td> + * <td><em>not applicable</em></td> + * </tr> + * </table> + * + * <p>Like any {@link BlockingQueue}, a <tt>BlockingDeque</tt> is thread safe, + * does not permit null elements, and may (or may not) be + * capacity-constrained. + * + * <p>A <tt>BlockingDeque</tt> implementation may be used directly as a FIFO + * <tt>BlockingQueue</tt>. The methods inherited from the + * <tt>BlockingQueue</tt> interface are precisely equivalent to + * <tt>BlockingDeque</tt> methods as indicated in the following table: + * + * <p> + * <table BORDER CELLPADDING=3 CELLSPACING=1> + * <tr> + * <td ALIGN=CENTER> <b><tt>BlockingQueue</tt> Method</b></td> + * <td ALIGN=CENTER> <b>Equivalent <tt>BlockingDeque</tt> Method</b></td> + * </tr> + * <tr> + * <td ALIGN=CENTER COLSPAN = 2> <b>Insert</b></td> + * </tr> + * <tr> + * <td>{@link #add(Object) add(e)}</td> + * <td>{@link #addLast(Object) addLast(e)}</td> + * </tr> + * <tr> + * <td>{@link #offer(Object) offer(e)}</td> + * <td>{@link #offerLast(Object) offerLast(e)}</td> + * </tr> + * <tr> + * <td>{@link #put(Object) put(e)}</td> + * <td>{@link #putLast(Object) putLast(e)}</td> + * </tr> + * <tr> + * <td>{@link #offer(Object, long, TimeUnit) offer(e, time, unit)}</td> + * <td>{@link #offerLast(Object, long, TimeUnit) offerLast(e, time, unit)}</td> + * </tr> + * <tr> + * <td ALIGN=CENTER COLSPAN = 2> <b>Remove</b></td> + * </tr> + * <tr> + * <td>{@link #remove() remove()}</td> + * <td>{@link #removeFirst() removeFirst()}</td> + * </tr> + * <tr> + * <td>{@link #poll() poll()}</td> + * <td>{@link #pollFirst() pollFirst()}</td> + * </tr> + * <tr> + * <td>{@link #take() take()}</td> + * <td>{@link #takeFirst() takeFirst()}</td> + * </tr> + * <tr> + * <td>{@link #poll(long, TimeUnit) poll(time, unit)}</td> + * <td>{@link #pollFirst(long, TimeUnit) pollFirst(time, unit)}</td> + * </tr> + * <tr> + * <td ALIGN=CENTER COLSPAN = 2> <b>Examine</b></td> + * </tr> + * <tr> + * <td>{@link #element() element()}</td> + * <td>{@link #getFirst() getFirst()}</td> + * </tr> + * <tr> + * <td>{@link #peek() peek()}</td> + * <td>{@link #peekFirst() peekFirst()}</td> + * </tr> + * </table> + * + * <p>Memory consistency effects: As with other concurrent + * collections, actions in a thread prior to placing an object into a + * {@code BlockingDeque} + * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> + * actions subsequent to the access or removal of that element from + * the {@code BlockingDeque} in another thread. + * + * <p>This interface is a member of the + * <a href="{@docRoot}/../technotes/guides/collections/index.html"> + * Java Collections Framework</a>. + * + * @since 1.6 + * @author Doug Lea + * @param <E> the type of elements held in this collection + */ +public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { + /* + * We have "diamond" multiple interface inheritance here, and that + * introduces ambiguities. Methods might end up with different + * specs depending on the branch chosen by javadoc. Thus a lot of + * methods specs here are copied from superinterfaces. + */ + + /** + * Inserts the specified element at the front of this deque if it is + * possible to do so immediately without violating capacity restrictions, + * throwing an <tt>IllegalStateException</tt> if no space is currently + * available. When using a capacity-restricted deque, it is generally + * preferable to use {@link #offerFirst(Object) offerFirst}. + * + * @param e the element to add + * @throws IllegalStateException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException {@inheritDoc} + */ + void addFirst(E e); + + /** + * Inserts the specified element at the end of this deque if it is + * possible to do so immediately without violating capacity restrictions, + * throwing an <tt>IllegalStateException</tt> if no space is currently + * available. When using a capacity-restricted deque, it is generally + * preferable to use {@link #offerLast(Object) offerLast}. + * + * @param e the element to add + * @throws IllegalStateException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException {@inheritDoc} + */ + void addLast(E e); + + /** + * Inserts the specified element at the front of this deque 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 deque, this method is generally + * preferable to the {@link #addFirst(Object) addFirst} method, which can + * fail to insert an element only by throwing an exception. + * + * @param e the element to add + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException {@inheritDoc} + */ + boolean offerFirst(E e); + + /** + * Inserts the specified element at the end of this deque 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 deque, this method is generally + * preferable to the {@link #addLast(Object) addLast} method, which can + * fail to insert an element only by throwing an exception. + * + * @param e the element to add + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException {@inheritDoc} + */ + boolean offerLast(E e); + + /** + * Inserts the specified element at the front of this deque, + * waiting if necessary 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 deque + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + void putFirst(E e) throws InterruptedException; + + /** + * Inserts the specified element at the end of this deque, + * waiting if necessary 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 deque + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + void putLast(E e) throws InterruptedException; + + /** + * Inserts the specified element at the front of this deque, + * 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> + * @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 ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + boolean offerFirst(E e, long timeout, TimeUnit unit) + throws InterruptedException; + + /** + * Inserts the specified element at the end of this deque, + * 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> + * @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 ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + boolean offerLast(E e, long timeout, TimeUnit unit) + throws InterruptedException; + + /** + * Retrieves and removes the first element of this deque, waiting + * if necessary until an element becomes available. + * + * @return the head of this deque + * @throws InterruptedException if interrupted while waiting + */ + E takeFirst() throws InterruptedException; + + /** + * Retrieves and removes the last element of this deque, waiting + * if necessary until an element becomes available. + * + * @return the tail of this deque + * @throws InterruptedException if interrupted while waiting + */ + E takeLast() throws InterruptedException; + + /** + * Retrieves and removes the first element of this deque, 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 deque, or <tt>null</tt> if the specified + * waiting time elapses before an element is available + * @throws InterruptedException if interrupted while waiting + */ + E pollFirst(long timeout, TimeUnit unit) + throws InterruptedException; + + /** + * Retrieves and removes the last element of this deque, 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 tail of this deque, or <tt>null</tt> if the specified + * waiting time elapses before an element is available + * @throws InterruptedException if interrupted while waiting + */ + E pollLast(long timeout, TimeUnit unit) + throws InterruptedException; + + /** + * Removes the first occurrence of the specified element from this deque. + * If the deque does not contain the element, it is unchanged. + * More formally, removes the first element <tt>e</tt> such that + * <tt>o.equals(e)</tt> (if such an element exists). + * Returns <tt>true</tt> if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). + * + * @param o element to be removed from this deque, if present + * @return <tt>true</tt> if an element was removed as a result of this call + * @throws ClassCastException if the class of the specified element + * is incompatible with this deque (optional) + * @throws NullPointerException if the specified element is null (optional) + */ + boolean removeFirstOccurrence(Object o); + + /** + * Removes the last occurrence of the specified element from this deque. + * If the deque does not contain the element, it is unchanged. + * More formally, removes the last element <tt>e</tt> such that + * <tt>o.equals(e)</tt> (if such an element exists). + * Returns <tt>true</tt> if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). + * + * @param o element to be removed from this deque, if present + * @return <tt>true</tt> if an element was removed as a result of this call + * @throws ClassCastException if the class of the specified element + * is incompatible with this deque (optional) + * @throws NullPointerException if the specified element is null (optional) + */ + boolean removeLastOccurrence(Object o); + + // *** BlockingQueue methods *** + + /** + * Inserts the specified element into the queue represented by this deque + * (in other words, at the tail of this deque) 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 deque, it is generally preferable to + * use {@link #offer(Object) offer}. + * + * <p>This method is equivalent to {@link #addLast(Object) addLast}. + * + * @param e the element to add + * @throws IllegalStateException {@inheritDoc} + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + boolean add(E e); + + /** + * Inserts the specified element into the queue represented by this deque + * (in other words, at the tail of this deque) 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 deque, this method is + * generally preferable to the {@link #add} method, which can fail to + * insert an element only by throwing an exception. + * + * <p>This method is equivalent to {@link #offerLast(Object) offerLast}. + * + * @param e the element to add + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + boolean offer(E e); + + /** + * Inserts the specified element into the queue represented by this deque + * (in other words, at the tail of this deque), waiting if necessary for + * space to become available. + * + * <p>This method is equivalent to {@link #putLast(Object) putLast}. + * + * @param e the element to add + * @throws InterruptedException {@inheritDoc} + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + void put(E e) throws InterruptedException; + + /** + * Inserts the specified element into the queue represented by this deque + * (in other words, at the tail of this deque), waiting up to the + * specified wait time if necessary for space to become available. + * + * <p>This method is equivalent to + * {@link #offerLast(Object,long,TimeUnit) offerLast}. + * + * @param e the element to add + * @return <tt>true</tt> if the element was added to this deque, else + * <tt>false</tt> + * @throws InterruptedException {@inheritDoc} + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + boolean offer(E e, long timeout, TimeUnit unit) + throws InterruptedException; + + /** + * Retrieves and removes the head of the queue represented by this deque + * (in other words, the first element of this deque). + * This method differs from {@link #poll poll} only in that it + * throws an exception if this deque is empty. + * + * <p>This method is equivalent to {@link #removeFirst() removeFirst}. + * + * @return the head of the queue represented by this deque + * @throws NoSuchElementException if this deque is empty + */ + E remove(); + + /** + * Retrieves and removes the head of the queue represented by this deque + * (in other words, the first element of this deque), or returns + * <tt>null</tt> if this deque is empty. + * + * <p>This method is equivalent to {@link #pollFirst()}. + * + * @return the head of this deque, or <tt>null</tt> if this deque is empty + */ + E poll(); + + /** + * Retrieves and removes the head of the queue represented by this deque + * (in other words, the first element of this deque), waiting if + * necessary until an element becomes available. + * + * <p>This method is equivalent to {@link #takeFirst() takeFirst}. + * + * @return the head of this deque + * @throws InterruptedException if interrupted while waiting + */ + E take() throws InterruptedException; + + /** + * Retrieves and removes the head of the queue represented by this deque + * (in other words, the first element of this deque), waiting up to the + * specified wait time if necessary for an element to become available. + * + * <p>This method is equivalent to + * {@link #pollFirst(long,TimeUnit) pollFirst}. + * + * @return the head of this deque, or <tt>null</tt> if the + * specified waiting time elapses before an element is available + * @throws InterruptedException if interrupted while waiting + */ + E poll(long timeout, TimeUnit unit) + throws InterruptedException; + + /** + * Retrieves, but does not remove, the head of the queue represented by + * this deque (in other words, the first element of this deque). + * This method differs from {@link #peek peek} only in that it throws an + * exception if this deque is empty. + * + * <p>This method is equivalent to {@link #getFirst() getFirst}. + * + * @return the head of this deque + * @throws NoSuchElementException if this deque is empty + */ + E element(); + + /** + * Retrieves, but does not remove, the head of the queue represented by + * this deque (in other words, the first element of this deque), or + * returns <tt>null</tt> if this deque is empty. + * + * <p>This method is equivalent to {@link #peekFirst() peekFirst}. + * + * @return the head of this deque, or <tt>null</tt> if this deque is empty + */ + E peek(); + + /** + * Removes the first occurrence of the specified element from this deque. + * If the deque does not contain the element, it is unchanged. + * More formally, removes the first element <tt>e</tt> such that + * <tt>o.equals(e)</tt> (if such an element exists). + * Returns <tt>true</tt> if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). + * + * <p>This method is equivalent to + * {@link #removeFirstOccurrence(Object) removeFirstOccurrence}. + * + * @param o element to be removed from this deque, if present + * @return <tt>true</tt> if this deque changed as a result of the call + * @throws ClassCastException if the class of the specified element + * is incompatible with this deque (optional) + * @throws NullPointerException if the specified element is null (optional) + */ + boolean remove(Object o); + + /** + * Returns <tt>true</tt> if this deque contains the specified element. + * More formally, returns <tt>true</tt> if and only if this deque 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 deque + * @return <tt>true</tt> if this deque contains the specified element + * @throws ClassCastException if the class of the specified element + * is incompatible with this deque (optional) + * @throws NullPointerException if the specified element is null (optional) + */ + public boolean contains(Object o); + + /** + * Returns the number of elements in this deque. + * + * @return the number of elements in this deque + */ + public int size(); + + /** + * Returns an iterator over the elements in this deque in proper sequence. + * The elements will be returned in order from first (head) to last (tail). + * + * @return an iterator over the elements in this deque in proper sequence + */ + Iterator<E> iterator(); + + // *** Stack methods *** + + /** + * Pushes an element onto the stack represented by this deque. In other + * words, inserts the element at the front of this deque unless it would + * violate capacity restrictions. + * + * <p>This method is equivalent to {@link #addFirst(Object) addFirst}. + * + * @throws IllegalStateException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException {@inheritDoc} + */ + void push(E e); +} diff --git a/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java b/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java index d01c097..85945b9 100644 --- a/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java +++ b/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java @@ -42,7 +42,7 @@ import java.util.Queue; * <td>{@link #add add(e)}</td> * <td>{@link #offer offer(e)}</td> * <td>{@link #put put(e)}</td> - * <td>{@link #offer offer(e, time, unit)}</td> + * <td>{@link #offer(Object, long, TimeUnit) offer(e, time, unit)}</td> * </tr> * <tr> * <td><b>Remove</b></td> @@ -156,7 +156,7 @@ public interface BlockingQueue<E> extends Queue<E> { * <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 offer}. + * use {@link #offer(Object) offer}. * * @param e the element to add * @return <tt>true</tt> (as specified by {@link Collection#add}) diff --git a/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java b/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java index cb5fb3f..1c157b4 100644 --- a/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java +++ b/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java @@ -613,6 +613,24 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } /** + * Creates a new, empty map with the specified initial capacity + * and load factor and with the default concurrencyLevel (16). + * + * @param initialCapacity The implementation performs internal + * sizing to accommodate this many elements. + * @param loadFactor the load factor threshold, used to control resizing. + * Resizing may be performed when the average number of elements per + * bin exceeds this threshold. + * @throws IllegalArgumentException if the initial capacity of + * elements is negative or the load factor is nonpositive + * + * @since 1.6 + */ + public ConcurrentHashMap(int initialCapacity, float loadFactor) { + this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL); + } + + /** * Creates a new, empty map with the specified initial capacity, * and with default load factor (0.75) and concurrencyLevel (16). * @@ -1112,7 +1130,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * setValue changes to the underlying map. */ final class WriteThroughEntry - extends SimpleEntry<K,V> + extends AbstractMap.SimpleEntry<K,V> { WriteThroughEntry(K k, V v) { super(k,v); @@ -1212,60 +1230,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } } - /** - * This duplicates java.util.AbstractMap.SimpleEntry until this class - * is made accessible. - */ - static class SimpleEntry<K,V> implements Entry<K,V> { - K key; - V value; - - public SimpleEntry(K key, V value) { - this.key = key; - this.value = value; - } - - public SimpleEntry(Entry<K,V> e) { - this.key = e.getKey(); - this.value = e.getValue(); - } - - public K getKey() { - return key; - } - - public V getValue() { - return value; - } - - public V setValue(V value) { - V oldValue = this.value; - this.value = value; - return oldValue; - } - - public boolean equals(Object o) { - if (!(o instanceof Map.Entry)) - return false; - Map.Entry e = (Map.Entry)o; - return eq(key, e.getKey()) && eq(value, e.getValue()); - } - - public int hashCode() { - return ((key == null) ? 0 : key.hashCode()) ^ - ((value == null) ? 0 : value.hashCode()); - } - - public String toString() { - return key + "=" + value; - } - - static boolean eq(Object o1, Object o2) { - return (o1 == null ? o2 == null : o1.equals(o2)); - } - } - - /* ---------------- Serialization Support -------------- */ + /* ---------------- Serialization Support -------------- */ /** * Save the state of the <tt>ConcurrentHashMap</tt> instance to a diff --git a/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java b/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java new file mode 100644 index 0000000..ee3534e --- /dev/null +++ b/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java @@ -0,0 +1,1246 @@ +/* + * Written by Doug Lea and Martin Buchholz with assistance from members of + * JCP JSR-166 Expert Group and released to the public domain, as explained + * at http://creativecommons.org/licenses/publicdomain + */ + +package java.util.concurrent; + +import java.util.AbstractCollection; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Deque; +import java.util.Iterator; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.atomic.AtomicReference; + +/** + * A concurrent linked-list implementation of a {@link Deque} + * (double-ended queue). Concurrent insertion, removal, and access + * operations execute safely across multiple threads. Iterators are + * <i>weakly consistent</i>, returning elements reflecting the state + * of the deque at some point at or since the creation of the + * iterator. They do <em>not</em> throw {@link + * ConcurrentModificationException}, and may proceed concurrently with + * other operations. + * + * <p>This class and its iterators implement all of the + * <em>optional</em> methods of the {@link Collection} and {@link + * Iterator} interfaces. Like most other concurrent collection + * implementations, this class does not permit the use of + * {@code null} elements. because some null arguments and return + * values cannot be reliably distinguished from the absence of + * elements. Arbitrarily, the {@link Collection#remove} method is + * mapped to {@code removeFirstOccurrence}, and {@link + * Collection#add} is mapped to {@code addLast}. + * + * <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 deques, determining the current number + * of elements requires a traversal of the elements. + * + * <p>This class is {@code Serializable}, but relies on default + * serialization mechanisms. Usually, it is a better idea for any + * serializable class using a {@code ConcurrentLinkedDeque} to instead + * serialize a snapshot of the elements obtained by method + * {@code toArray}. + * + * @author Doug Lea + * @author Martin Buchholz + * @param <E> the type of elements held in this collection + */ + +public class ConcurrentLinkedDeque<E> + extends AbstractCollection<E> + implements Deque<E>, java.io.Serializable { + + /* + * This is an implementation of a concurrent lock-free deque + * supporting interior removes but not interior insertions, as + * required to fully support the Deque interface. + * + * We extend the techniques developed for + * ConcurrentLinkedQueue and LinkedTransferQueue + * (see the internal docs for those classes). + * + * At any time, there is precisely one "first" active node with a + * null prev pointer. Similarly there is one "last" active node + * with a null next pointer. New nodes are simply enqueued by + * null-CASing. + * + * A node p is considered "active" if it either contains an + * element, or is an end node and neither next nor prev pointers + * are self-links: + * + * p.item != null || + * (p.prev == null && p.next != p) || + * (p.next == null && p.prev != p) + * + * The head and tail pointers are only approximations to the start + * and end of the deque. The first node can always be found by + * following prev pointers from head; likewise for tail. However, + * head and tail may be pointing at deleted nodes that have been + * unlinked and so may not be reachable from any live node. + * + * There are 3 levels of node deletion: + * - logical deletion atomically removes the element + * - "unlinking" makes a deleted node unreachable from active + * nodes, and thus eventually reclaimable by GC + * - "gc-unlinking" further does the reverse of making active + * nodes unreachable from deleted nodes, making it easier for + * the GC to reclaim future deleted nodes + * + * TODO: find a better name for "gc-unlinked" + * + * Logical deletion of a node simply involves CASing its element + * to null. Physical deletion is merely an optimization (albeit a + * critical one), and can be performed at our convenience. At any + * time, the set of non-logically-deleted nodes maintained by prev + * and next links are identical, that is the live elements found + * via next links from the first node is equal to the elements + * found via prev links from the last node. However, this is not + * true for nodes that have already been logically deleted - such + * nodes may only be reachable in one direction. + * + * When a node is dequeued at either end, e.g. via poll(), we + * would like to break any references from the node to live nodes, + * to stop old garbage from causing retention of new garbage with + * a generational or conservative GC. We develop further the + * self-linking trick that was very effective in other concurrent + * collection classes. The idea is to replace prev and next + * pointers to active nodes with special values that are + * interpreted to mean off-the-list-at-one-end. These are + * approximations, but good enough to preserve the properties we + * want in our traversals, e.g. we guarantee that a traversal will + * never hit the same element twice, but we don't guarantee + * whether a traversal that runs out of elements will be able to + * see more elements later after more elements are added at that + * end. Doing gc-unlinking safely is particularly tricky, since + * any node can be in use indefinitely (for example by an + * iterator). We must make sure that the nodes pointed at by + * head/tail do not get gc-unlinked, since head/tail are needed to + * get "back on track" by other nodes that are gc-unlinked. + * gc-unlinking accounts for much of the implementation complexity. + * + * Since neither unlinking nor gc-unlinking are necessary for + * correctness, there are many implementation choices regarding + * frequency (eagerness) of these operations. Since volatile + * reads are likely to be much cheaper than CASes, saving CASes by + * unlinking multiple adjacent nodes at a time may be a win. + * gc-unlinking can be performed rarely and still be effective, + * since it is most important that long chains of deleted nodes + * are occasionally broken. + * + * The actual representation we use is that p.next == p means to + * goto the first node, and p.next == null && p.prev == p means + * that the iteration is at an end and that p is a (final static) + * dummy node, NEXT_TERMINATOR, and not the last active node. + * Finishing the iteration when encountering such a TERMINATOR is + * good enough for read-only traversals. When the last active + * node is desired, for example when enqueueing, goto tail and + * continue traversal. + * + * The implementation is completely directionally symmetrical, + * except that most public methods that iterate through the list + * follow next pointers ("forward" direction). + * + * There is one desirable property we would like to have, but + * don't: it is possible, when an addFirst(A) is racing with + * pollFirst() removing B, for an iterating observer to see A B C + * and subsequently see A C, even though no interior removes are + * ever performed. I believe this wart can only be removed at + * significant runtime cost. + * + * Empirically, microbenchmarks suggest that this class adds about + * 40% overhead relative to ConcurrentLinkedQueue, which feels as + * good as we can hope for. + */ + + /** + * A node from which the first node on list (that is, the unique + * node with node.prev == null) can be reached in O(1) time. + * Invariants: + * - the first node is always O(1) reachable from head via prev links + * - all live nodes are reachable from the first node via succ() + * - head != null + * - (tmp = head).next != tmp || tmp != head + * Non-invariants: + * - head.item may or may not be null + * - head may not be reachable from the first or last node, or from tail + */ + private transient volatile Node<E> head = new Node<E>(null); + + private final static Node<Object> PREV_TERMINATOR, NEXT_TERMINATOR; + + static { + PREV_TERMINATOR = new Node<Object>(null); + PREV_TERMINATOR.next = PREV_TERMINATOR; + NEXT_TERMINATOR = new Node<Object>(null); + NEXT_TERMINATOR.prev = NEXT_TERMINATOR; + } + + @SuppressWarnings("unchecked") + Node<E> prevTerminator() { + return (Node<E>) PREV_TERMINATOR; + } + + @SuppressWarnings("unchecked") + Node<E> nextTerminator() { + return (Node<E>) NEXT_TERMINATOR; + } + + /** + * A node from which the last node on list (that is, the unique + * node with node.next == null) can be reached in O(1) time. + * Invariants: + * - the last node is always O(1) reachable from tail via next links + * - all live nodes are reachable from the last node via pred() + * - tail != null + * Non-invariants: + * - tail.item may or may not be null + * - tail may not be reachable from the first or last node, or from head + */ + private transient volatile Node<E> tail = head; + + static final class Node<E> { + volatile Node<E> prev; + volatile E item; + volatile Node<E> next; + + Node(E item) { + // Piggyback on imminent casNext() or casPrev() + lazySetItem(item); + } + + boolean casItem(E cmp, E val) { + return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); + } + + void lazySetItem(E val) { + UNSAFE.putOrderedObject(this, itemOffset, val); + } + + void lazySetNext(Node<E> val) { + UNSAFE.putOrderedObject(this, nextOffset, val); + } + + boolean casNext(Node<E> cmp, Node<E> val) { + return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); + } + + void lazySetPrev(Node<E> val) { + UNSAFE.putOrderedObject(this, prevOffset, val); + } + + boolean casPrev(Node<E> cmp, Node<E> val) { + return UNSAFE.compareAndSwapObject(this, prevOffset, cmp, val); + } + + // Unsafe mechanics + + private static final sun.misc.Unsafe UNSAFE = + sun.misc.Unsafe.getUnsafe(); + private static final long prevOffset = + objectFieldOffset(UNSAFE, "prev", Node.class); + private static final long itemOffset = + objectFieldOffset(UNSAFE, "item", Node.class); + private static final long nextOffset = + objectFieldOffset(UNSAFE, "next", Node.class); + } + + /** + * Links e as first element. + */ + private void linkFirst(E e) { + checkNotNull(e); + final Node<E> newNode = new Node<E>(e); + + retry: + for (;;) { + for (Node<E> h = head, p = h;;) { + Node<E> q = p.prev; + if (q == null) { + if (p.next == p) + continue retry; + newNode.lazySetNext(p); // CAS piggyback + if (p.casPrev(null, newNode)) { + if (p != h) // hop two nodes at a time + casHead(h, newNode); + return; + } else { + p = p.prev; // lost CAS race to another thread + } + } + else if (p == q) + continue retry; + else + p = q; + } + } + } + + /** + * Links e as last element. + */ + private void linkLast(E e) { + checkNotNull(e); + final Node<E> newNode = new Node<E>(e); + + retry: + for (;;) { + for (Node<E> t = tail, p = t;;) { + Node<E> q = p.next; + if (q == null) { + if (p.prev == p) + continue retry; + newNode.lazySetPrev(p); // CAS piggyback + if (p.casNext(null, newNode)) { + if (p != t) // hop two nodes at a time + casTail(t, newNode); + return; + } else { + p = p.next; // lost CAS race to another thread + } + } + else if (p == q) + continue retry; + else + p = q; + } + } + } + + // TODO: Is there a better cheap way of performing some cleanup + // operation "occasionally"? + static class Count { + int count = 0; + } + private final static ThreadLocal<Count> tlc = + new ThreadLocal<Count>() { + protected Count initialValue() { return new Count(); } + }; + private static boolean shouldGCUnlinkOccasionally() { + return (tlc.get().count++ & 0x3) == 0; + } + + private final static int HOPS = 2; + + /** + * Unlinks non-null node x. + */ + void unlink(Node<E> x) { + assert x != null; + assert x.item == null; + assert x != PREV_TERMINATOR; + assert x != NEXT_TERMINATOR; + + final Node<E> prev = x.prev; + final Node<E> next = x.next; + if (prev == null) { + unlinkFirst(x, next); + } else if (next == null) { + unlinkLast(x, prev); + } else { + // Unlink interior node. + // + // This is the common case, since a series of polls at the + // same end will be "interior" removes, except perhaps for + // the first one, since end nodes cannot be physically removed. + // + // At any time, all active nodes are mutually reachable by + // following a sequence of either next or prev pointers. + // + // Our strategy is to find the unique active predecessor + // and successor of x. Try to fix up their links so that + // they point to each other, leaving x unreachable from + // active nodes. If successful, and if x has no live + // predecessor/successor, we additionally try to leave + // active nodes unreachable from x, by rechecking that + // the status of predecessor and successor are unchanged + // and ensuring that x is not reachable from tail/head, + // before setting x's prev/next links to their logical + // approximate replacements, self/TERMINATOR. + Node<E> activePred, activeSucc; + boolean isFirst, isLast; + int hops = 1; + + // Find active predecessor + for (Node<E> p = prev;; ++hops) { + if (p.item != null) { + activePred = p; + isFirst = false; + break; + } + Node<E> q = p.prev; + if (q == null) { + if (p == p.next) + return; + activePred = p; + isFirst = true; + break; + } + else if (p == q) + return; + else + p = q; + } + + // Find active successor + for (Node<E> p = next;; ++hops) { + if (p.item != null) { + activeSucc = p; + isLast = false; + break; + } + Node<E> q = p.next; + if (q == null) { + if (p == p.prev) + return; + activeSucc = p; + isLast = true; + break; + } + else if (p == q) + return; + else + p = q; + } + + // TODO: better HOP heuristics + if (hops < HOPS + // always squeeze out interior deleted nodes + && (isFirst | isLast)) + return; + + // Squeeze out deleted nodes between activePred and + // activeSucc, including x. + skipDeletedSuccessors(activePred); + skipDeletedPredecessors(activeSucc); + + // Try to gc-unlink, if possible + if ((isFirst | isLast) && + //shouldGCUnlinkOccasionally() && + + // Recheck expected state of predecessor and successor + (activePred.next == activeSucc) && + (activeSucc.prev == activePred) && + (isFirst ? activePred.prev == null : activePred.item != null) && + (isLast ? activeSucc.next == null : activeSucc.item != null)) { + + // Ensure x is not reachable from head or tail + updateHead(); + updateTail(); + x.lazySetPrev(isFirst ? prevTerminator() : x); + x.lazySetNext(isLast ? nextTerminator() : x); + } + } + } + + /** + * Unlinks non-null first node. + */ + private void unlinkFirst(Node<E> first, Node<E> next) { + assert first != null && next != null && first.item == null; + Node<E> o = null, p = next; + for (int hops = 0;; ++hops) { + Node<E> q; + if (p.item != null || (q = p.next) == null) { + if (hops >= HOPS) { + if (p == p.prev) + return; + if (first.casNext(next, p)) { + skipDeletedPredecessors(p); + if (//shouldGCUnlinkOccasionally() && + first.prev == null && + (p.next == null || p.item != null) && + p.prev == first) { + + updateHead(); + updateTail(); + o.lazySetNext(o); + o.lazySetPrev(prevTerminator()); + } + } + } + return; + } + else if (p == q) + return; + else { + o = p; + p = q; + } + } + } + + /** + * Unlinks non-null last node. + */ + private void unlinkLast(Node<E> last, Node<E> prev) { + assert last != null && prev != null && last.item == null; + Node<E> o = null, p = prev; + for (int hops = 0;; ++hops) { + Node<E> q; + if (p.item != null || (q = p.prev) == null) { + if (hops >= HOPS) { + if (p == p.next) + return; + if (last.casPrev(prev, p)) { + skipDeletedSuccessors(p); + if (//shouldGCUnlinkOccasionally() && + last.next == null && + (p.prev == null || p.item != null) && + p.next == last) { + + updateHead(); + updateTail(); + o.lazySetPrev(o); + o.lazySetNext(nextTerminator()); + } + } + } + return; + } + else if (p == q) + return; + else { + o = p; + p = q; + } + } + } + + private final void updateHead() { + first(); + } + + private final void updateTail() { + last(); + } + + private void skipDeletedPredecessors(Node<E> x) { + whileActive: + do { + Node<E> prev = x.prev; + assert prev != null; + assert x != NEXT_TERMINATOR; + assert x != PREV_TERMINATOR; + Node<E> p = prev; + findActive: + for (;;) { + if (p.item != null) + break findActive; + Node<E> q = p.prev; + if (q == null) { + if (p.next == p) + continue whileActive; + break findActive; + } + else if (p == q) + continue whileActive; + else + p = q; + } + + // found active CAS target + if (prev == p || x.casPrev(prev, p)) + return; + + } while (x.item != null || x.next == null); + } + + private void skipDeletedSuccessors(Node<E> x) { + whileActive: + do { + Node<E> next = x.next; + assert next != null; + assert x != NEXT_TERMINATOR; + assert x != PREV_TERMINATOR; + Node<E> p = next; + findActive: + for (;;) { + if (p.item != null) + break findActive; + Node<E> q = p.next; + if (q == null) { + if (p.prev == p) + continue whileActive; + break findActive; + } + else if (p == q) + continue whileActive; + else + p = q; + } + + // found active CAS target + if (next == p || x.casNext(next, p)) + return; + + } while (x.item != null || x.prev == null); + } + + /** + * Returns the successor of p, or the first 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) { + // TODO: should we skip deleted nodes here? + Node<E> q = p.next; + return (p == q) ? first() : q; + } + + /** + * Returns the predecessor of p, or the last node if p.prev 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> pred(Node<E> p) { + Node<E> q = p.prev; + return (p == q) ? last() : q; + } + + /** + * Returns the first node, the unique node which has a null prev link. + * The returned node may or may not be logically deleted. + * Guarantees that head is set to the returned node. + */ + Node<E> first() { + retry: + for (;;) { + for (Node<E> h = head, p = h;;) { + Node<E> q = p.prev; + if (q == null) { + if (p == h + // It is possible that p is PREV_TERMINATOR, + // but if so, the CAS will fail. + || casHead(h, p)) + return p; + else + continue retry; + } else if (p == q) { + continue retry; + } else { + p = q; + } + } + } + } + + /** + * Returns the last node, the unique node which has a null next link. + * The returned node may or may not be logically deleted. + * Guarantees that tail is set to the returned node. + */ + Node<E> last() { + retry: + for (;;) { + for (Node<E> t = tail, p = t;;) { + Node<E> q = p.next; + if (q == null) { + if (p == t + // It is possible that p is NEXT_TERMINATOR, + // but if so, the CAS will fail. + || casTail(t, p)) + return p; + else + continue retry; + } else if (p == q) { + continue retry; + } else { + p = q; + } + } + } + } + + // Minor convenience utilities + + /** + * Throws NullPointerException if argument is null. + * + * @param v the element + */ + private static void checkNotNull(Object v) { + if (v == null) + throw new NullPointerException(); + } + + /** + * Returns element unless it is null, in which case throws + * NoSuchElementException. + * + * @param v the element + * @return the element + */ + private E screenNullResult(E v) { + if (v == null) + throw new NoSuchElementException(); + return v; + } + + /** + * Creates an array list and fills it with elements of this list. + * Used by toArray. + * + * @return the arrayList + */ + private ArrayList<E> toArrayList() { + ArrayList<E> c = new ArrayList<E>(); + for (Node<E> p = first(); p != null; p = succ(p)) { + E item = p.item; + if (item != null) + c.add(item); + } + return c; + } + + // Fields and constructors + + private static final long serialVersionUID = 876323262645176354L; + + /** + * Constructs an empty deque. + */ + public ConcurrentLinkedDeque() {} + + /** + * Constructs a deque 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 the specified collection or any + * of its elements are null + */ + public ConcurrentLinkedDeque(Collection<? extends E> c) { + this(); + addAll(c); + } + + /** + * Inserts the specified element at the front of this deque. + * + * @throws NullPointerException {@inheritDoc} + */ + public void addFirst(E e) { + linkFirst(e); + } + + /** + * Inserts the specified element at the end of this deque. + * This is identical in function to the {@code add} method. + * + * @throws NullPointerException {@inheritDoc} + */ + public void addLast(E e) { + linkLast(e); + } + + /** + * Inserts the specified element at the front of this deque. + * + * @return {@code true} always + * @throws NullPointerException {@inheritDoc} + */ + public boolean offerFirst(E e) { + linkFirst(e); + return true; + } + + /** + * Inserts the specified element at the end of this deque. + * + * <p>This method is equivalent to {@link #add}. + * + * @return {@code true} always + * @throws NullPointerException {@inheritDoc} + */ + public boolean offerLast(E e) { + linkLast(e); + return true; + } + + public E peekFirst() { + for (Node<E> p = first(); p != null; p = succ(p)) { + E item = p.item; + if (item != null) + return item; + } + return null; + } + + public E peekLast() { + for (Node<E> p = last(); p != null; p = pred(p)) { + E item = p.item; + if (item != null) + return item; + } + return null; + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E getFirst() { + return screenNullResult(peekFirst()); + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E getLast() { + return screenNullResult(peekLast()); + } + + public E pollFirst() { + for (Node<E> p = first(); p != null; p = succ(p)) { + E item = p.item; + if (item != null && p.casItem(item, null)) { + unlink(p); + return item; + } + } + return null; + } + + public E pollLast() { + for (Node<E> p = last(); p != null; p = pred(p)) { + E item = p.item; + if (item != null && p.casItem(item, null)) { + unlink(p); + return item; + } + } + return null; + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E removeFirst() { + return screenNullResult(pollFirst()); + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E removeLast() { + return screenNullResult(pollLast()); + } + + // *** Queue and stack methods *** + + /** + * Inserts the specified element at the tail of this deque. + * + * @return {@code true} (as specified by {@link Queue#offer}) + * @throws NullPointerException if the specified element is null + */ + public boolean offer(E e) { + return offerLast(e); + } + + /** + * Inserts the specified element at the tail of this deque. + * + * @return {@code true} (as specified by {@link Collection#add}) + * @throws NullPointerException if the specified element is null + */ + public boolean add(E e) { + return offerLast(e); + } + + public E poll() { return pollFirst(); } + public E remove() { return removeFirst(); } + public E peek() { return peekFirst(); } + public E element() { return getFirst(); } + public void push(E e) { addFirst(e); } + public E pop() { return removeFirst(); } + + /** + * Removes the first element {@code e} such that + * {@code o.equals(e)}, if such an element exists in this deque. + * If the deque does not contain the element, it is unchanged. + * + * @param o element to be removed from this deque, if present + * @return {@code true} if the deque contained the specified element + * @throws NullPointerException if the specified element is {@code null} + */ + public boolean removeFirstOccurrence(Object o) { + checkNotNull(o); + for (Node<E> p = first(); p != null; p = succ(p)) { + E item = p.item; + if (item != null && o.equals(item) && p.casItem(item, null)) { + unlink(p); + return true; + } + } + return false; + } + + /** + * Removes the last element {@code e} such that + * {@code o.equals(e)}, if such an element exists in this deque. + * If the deque does not contain the element, it is unchanged. + * + * @param o element to be removed from this deque, if present + * @return {@code true} if the deque contained the specified element + * @throws NullPointerException if the specified element is {@code null} + */ + public boolean removeLastOccurrence(Object o) { + checkNotNull(o); + for (Node<E> p = last(); p != null; p = pred(p)) { + E item = p.item; + if (item != null && o.equals(item) && p.casItem(item, null)) { + unlink(p); + return true; + } + } + return false; + } + + /** + * Returns {@code true} if this deque contains at least one + * element {@code e} such that {@code o.equals(e)}. + * + * @param o element whose presence in this deque is to be tested + * @return {@code true} if this deque contains the specified element + */ + public boolean contains(Object o) { + if (o == null) return false; + for (Node<E> p = first(); p != null; p = succ(p)) { + E item = p.item; + if (item != null && o.equals(item)) + return true; + } + return false; + } + + /** + * Returns {@code true} if this collection contains no elements. + * + * @return {@code true} if this collection contains no elements + */ + public boolean isEmpty() { + return peekFirst() == null; + } + + /** + * Returns the number of elements in this deque. If this deque + * contains more than {@code Integer.MAX_VALUE} elements, it + * 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 deques, determining the current + * number of elements requires traversing them all to count them. + * Additionally, it is possible for the size to change during + * execution of this method, in which case the returned result + * will be inaccurate. Thus, this method is typically not very + * useful in concurrent applications. + * + * @return the number of elements in this deque + */ + public int size() { + long count = 0; + for (Node<E> p = first(); p != null; p = succ(p)) + if (p.item != null) + ++count; + return (count >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) count; + } + + /** + * Removes the first element {@code e} such that + * {@code o.equals(e)}, if such an element exists in this deque. + * If the deque does not contain the element, it is unchanged. + * + * @param o element to be removed from this deque, if present + * @return {@code true} if the deque contained the specified element + * @throws NullPointerException if the specified element is {@code null} + */ + public boolean remove(Object o) { + return removeFirstOccurrence(o); + } + + /** + * Appends all of the elements in the specified collection to the end of + * this deque, in the order that they are returned by the specified + * collection's iterator. The behavior of this operation is undefined if + * the specified collection is modified while the operation is in + * progress. (This implies that the behavior of this call is undefined if + * the specified Collection is this deque, and this deque is nonempty.) + * + * @param c the elements to be inserted into this deque + * @return {@code true} if this deque changed as a result of the call + * @throws NullPointerException if {@code c} or any element within it + * is {@code null} + */ + public boolean addAll(Collection<? extends E> c) { + Iterator<? extends E> it = c.iterator(); + if (!it.hasNext()) + return false; + do { + addLast(it.next()); + } while (it.hasNext()); + return true; + } + + /** + * Removes all of the elements from this deque. + */ + public void clear() { + while (pollFirst() != null) + ; + } + + /** + * Returns an array containing all of the elements in this deque, 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 deque. (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 deque + */ + public Object[] toArray() { + return toArrayList().toArray(); + } + + /** + * Returns an array containing all of the elements in this deque, + * in proper sequence (from first to last element); the runtime + * type of the returned array is that of the specified array. If + * the deque 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 deque. + * + * <p>If this deque fits in the specified array with room to spare + * (i.e., the array has more elements than this deque), the element in + * the array immediately following the end of the deque 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 deque known to contain only strings. + * The following code can be used to dump the deque 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 deque 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 deque + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this deque + * @throws NullPointerException if the specified array is null + */ + public <T> T[] toArray(T[] a) { + return toArrayList().toArray(a); + } + + /** + * Returns an iterator over the elements in this deque in proper sequence. + * The elements will be returned in order from first (head) to last (tail). + * + * <p>The returned {@code Iterator} is a "weakly consistent" iterator that + * will never throw {@link java.util.ConcurrentModificationException + * 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 deque in proper sequence + */ + public Iterator<E> iterator() { + return new Itr(); + } + + /** + * Returns an iterator over the elements in this deque in reverse + * sequential order. The elements will be returned in order from + * last (tail) to first (head). + * + * <p>The returned {@code Iterator} is a "weakly consistent" iterator that + * will never throw {@link java.util.ConcurrentModificationException + * ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + */ + public Iterator<E> descendingIterator() { + return new DescendingItr(); + } + + private abstract class AbstractItr implements Iterator<E> { + /** + * Next node to return item for. + */ + private Node<E> nextNode; + + /** + * nextItem holds on to item fields because once we claim + * 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; + + /** + * Node returned by most recent call to next. Needed by remove. + * Reset to null if this element is deleted by a call to remove. + */ + private Node<E> lastRet; + + abstract Node<E> startNode(); + abstract Node<E> nextNode(Node<E> p); + + AbstractItr() { + advance(); + } + + /** + * Sets nextNode and nextItem to next valid node, or to null + * if no such. + */ + private void advance() { + lastRet = nextNode; + + Node<E> p = (nextNode == null) ? startNode() : nextNode(nextNode); + for (;; p = nextNode(p)) { + if (p == null) { + // p might be active end or TERMINATOR node; both are OK + nextNode = null; + nextItem = null; + break; + } + E item = p.item; + if (item != null) { + nextNode = p; + nextItem = item; + break; + } + } + } + + public boolean hasNext() { + return nextItem != null; + } + + public E next() { + E item = nextItem; + if (item == null) throw new NoSuchElementException(); + advance(); + return item; + } + + public void remove() { + Node<E> l = lastRet; + if (l == null) throw new IllegalStateException(); + l.item = null; + unlink(l); + lastRet = null; + } + } + + /** Forward iterator */ + private class Itr extends AbstractItr { + Node<E> startNode() { return first(); } + Node<E> nextNode(Node<E> p) { return succ(p); } + } + + /** Descending iterator */ + private class DescendingItr extends AbstractItr { + Node<E> startNode() { return last(); } + Node<E> nextNode(Node<E> p) { return pred(p); } + } + + /** + * Save the state to a stream (that is, serialize it). + * + * @serialData All of the elements (each an {@code E}) in + * the proper order, followed by a null + * @param s the stream + */ + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + + // Write out any hidden stuff + s.defaultWriteObject(); + + // Write out all elements in the proper order. + for (Node<E> p = first(); p != null; p = succ(p)) { + Object item = p.item; + if (item != null) + s.writeObject(item); + } + + // Use trailing null as sentinel + s.writeObject(null); + } + + /** + * Reconstitute the Queue instance from a stream (that is, + * deserialize it). + * @param s the stream + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + // Read in capacity, and any hidden stuff + s.defaultReadObject(); + tail = head = new Node<E>(null); + // Read in all elements and place in queue + for (;;) { + @SuppressWarnings("unchecked") + E item = (E)s.readObject(); + if (item == null) + break; + else + offer(item); + } + } + + // Unsafe mechanics + + private static final sun.misc.Unsafe UNSAFE = + sun.misc.Unsafe.getUnsafe(); + private static final long headOffset = + objectFieldOffset(UNSAFE, "head", ConcurrentLinkedDeque.class); + private static final long tailOffset = + objectFieldOffset(UNSAFE, "tail", ConcurrentLinkedDeque.class); + + private boolean casHead(Node<E> cmp, Node<E> val) { + return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); + } + + private boolean casTail(Node<E> cmp, Node<E> val) { + return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val); + } + + static long objectFieldOffset(sun.misc.Unsafe UNSAFE, + String field, Class<?> klazz) { + try { + return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field)); + } catch (NoSuchFieldException e) { + // Convert Exception to corresponding Error + NoSuchFieldError error = new NoSuchFieldError(field); + error.initCause(e); + throw error; + } + } +} diff --git a/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java b/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java index 2253823..2d222c1 100644 --- a/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java +++ b/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java @@ -5,9 +5,13 @@ */ package java.util.concurrent; -import java.util.*; -import java.util.concurrent.atomic.*; +import java.util.AbstractQueue; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Queue; // BEGIN android-note // removed link to collections framework docs @@ -126,34 +130,34 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> * 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> - nextUpdater = - AtomicReferenceFieldUpdater.newUpdater - (Node.class, Node.class, "next"); - private static final - AtomicReferenceFieldUpdater<Node, Object> - itemUpdater = - AtomicReferenceFieldUpdater.newUpdater - (Node.class, Object.class, "item"); - - - Node(E item) { setItem(item); } + Node(E item) { + // Piggyback on imminent casNext() + lazySetItem(item); + } E getItem() { return item; } boolean casItem(E cmp, E val) { - return itemUpdater.compareAndSet(this, cmp, val); + return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); } void setItem(E val) { - itemUpdater.set(this, val); + item = val; + } + + void lazySetItem(E val) { + UNSAFE.putOrderedObject(this, itemOffset, val); + } + + void lazySetNext(Node<E> val) { + UNSAFE.putOrderedObject(this, nextOffset, val); } Node<E> getNext() { @@ -161,42 +165,45 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> } boolean casNext(Node<E> cmp, Node<E> val) { - return nextUpdater.compareAndSet(this, cmp, val); + return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); } - void setNext(Node<E> val) { - nextUpdater.set(this, val); - } + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE = + sun.misc.Unsafe.getUnsafe(); + private static final long nextOffset = + objectFieldOffset(UNSAFE, "next", Node.class); + private static final long itemOffset = + objectFieldOffset(UNSAFE, "item", Node.class); } - private static final - AtomicReferenceFieldUpdater<ConcurrentLinkedQueue, Node> - tailUpdater = - AtomicReferenceFieldUpdater.newUpdater - (ConcurrentLinkedQueue.class, Node.class, "tail"); - private static final - AtomicReferenceFieldUpdater<ConcurrentLinkedQueue, Node> - headUpdater = - AtomicReferenceFieldUpdater.newUpdater - (ConcurrentLinkedQueue.class, Node.class, "head"); - - private boolean casTail(Node<E> cmp, Node<E> val) { - return tailUpdater.compareAndSet(this, cmp, val); - } - - private boolean casHead(Node<E> cmp, Node<E> val) { - return headUpdater.compareAndSet(this, cmp, val); - } - - - /** - * Pointer to first node, initialized to a dummy node. + * A node from which the first live (non-deleted) node (if any) + * can be reached in O(1) time. + * Invariants: + * - all live nodes are reachable from head via succ() + * - head != null + * - (tmp = head).next != tmp || tmp != head + * Non-invariants: + * - head.item may or may not be null. + * - it is permitted for tail to lag behind head, that is, for tail + * to not be reachable from head! */ private transient volatile Node<E> head = new Node<E>(null); - /** Pointer to last node on list */ + /** + * A node from which the last node on list (that is, the unique + * node with node.next == null) can be reached in O(1) time. + * Invariants: + * - the last node is always reachable from tail via succ() + * - tail != null + * Non-invariants: + * - tail.item may or may not be null. + * - it is permitted for tail to lag behind head, that is, for tail + * to not be reachable from head! + * - tail.next may or may not be self-pointing to tail. + */ private transient volatile Node<E> tail = head; @@ -214,8 +221,8 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> * of its elements are null */ public ConcurrentLinkedQueue(Collection<? extends E> c) { - for (Iterator<? extends E> it = c.iterator(); it.hasNext();) - add(it.next()); + for (E e : c) + add(e); } // Have to override just to update the javadoc @@ -231,7 +238,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> } /** - * We don't bother to update head or tail pointers if less than + * We don't bother to update head or tail pointers if fewer than * HOPS links from "true" location. We assume that volatile * writes are significantly more expensive than volatile reads. */ @@ -243,7 +250,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> */ final void updateHead(Node<E> h, Node<E> p) { if (h != p && casHead(h, p)) - h.setNext(h); + h.lazySetNext(h); } /** @@ -328,10 +335,12 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> } /** - * Returns the first actual (non-header) node on list. This is yet - * another variant of poll/peek; here returning out the first - * node, not element (so we cannot collapse with peek() without - * introducing race.) + * Returns the first live (non-deleted) node on list, or null if none. + * This is yet another variant of poll/peek; here returning the + * first node, not element. We could make peek() a wrapper around + * first(), but that would cost an extra volatile read of item, + * and the need to add a retry loop to deal with the possibility + * of losing a race to a concurrent poll(). */ Node<E> first() { Node<E> h = head; @@ -422,7 +431,9 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> 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); @@ -522,7 +533,8 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> /** * 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 ConcurrentModificationException}, + * will never throw {@link java.util.ConcurrentModificationException + * 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. @@ -658,4 +670,35 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> } } + // Unsafe mechanics + + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); + private static final long headOffset = + objectFieldOffset(UNSAFE, "head", ConcurrentLinkedQueue.class); + private static final long tailOffset = + objectFieldOffset(UNSAFE, "tail", ConcurrentLinkedQueue.class); + + private boolean casTail(Node<E> cmp, Node<E> val) { + return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val); + } + + private boolean casHead(Node<E> cmp, Node<E> val) { + return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); + } + + private void lazySetHead(Node<E> val) { + UNSAFE.putOrderedObject(this, headOffset, val); + } + + static long objectFieldOffset(sun.misc.Unsafe UNSAFE, + String field, Class<?> klazz) { + try { + return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field)); + } catch (NoSuchFieldException e) { + // Convert Exception to corresponding Error + NoSuchFieldError error = new NoSuchFieldError(field); + error.initCause(e); + throw error; + } + } } diff --git a/concurrent/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java b/concurrent/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java new file mode 100644 index 0000000..7d86afb --- /dev/null +++ b/concurrent/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java @@ -0,0 +1,148 @@ +/* + * 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; +import java.util.*; + +/** + * A {@link ConcurrentMap} supporting {@link NavigableMap} operations, + * and recursively so for its navigable sub-maps. + * + * <p>This interface is a member of the + * <a href="{@docRoot}/../technotes/guides/collections/index.html"> + * Java Collections Framework</a>. + * + * @author Doug Lea + * @param <K> the type of keys maintained by this map + * @param <V> the type of mapped values + * @since 1.6 + */ +public interface ConcurrentNavigableMap<K,V> + extends ConcurrentMap<K,V>, NavigableMap<K,V> +{ + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + ConcurrentNavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, + K toKey, boolean toInclusive); + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + ConcurrentNavigableMap<K,V> headMap(K toKey, boolean inclusive); + + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + ConcurrentNavigableMap<K,V> tailMap(K fromKey, boolean inclusive); + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + ConcurrentNavigableMap<K,V> subMap(K fromKey, K toKey); + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + ConcurrentNavigableMap<K,V> headMap(K toKey); + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + ConcurrentNavigableMap<K,V> tailMap(K fromKey); + + /** + * Returns a reverse order view of the mappings contained in this map. + * The descending map is backed by this map, so changes to the map are + * reflected in the descending map, and vice-versa. + * + * <p>The returned map has an ordering equivalent to + * <tt>{@link Collections#reverseOrder(Comparator) Collections.reverseOrder}(comparator())</tt>. + * The expression {@code m.descendingMap().descendingMap()} returns a + * view of {@code m} essentially equivalent to {@code m}. + * + * @return a reverse order view of this map + */ + ConcurrentNavigableMap<K,V> descendingMap(); + + /** + * Returns a {@link NavigableSet} view of the keys contained in this map. + * The set's iterator returns the keys in ascending order. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. The set supports element + * removal, which removes the corresponding mapping from the map, + * via the {@code Iterator.remove}, {@code Set.remove}, + * {@code removeAll}, {@code retainAll}, and {@code clear} + * operations. It does not support the {@code add} or {@code addAll} + * operations. + * + * <p>The view's {@code iterator} is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + * + * @return a navigable set view of the keys in this map + */ + public NavigableSet<K> navigableKeySet(); + + /** + * Returns a {@link NavigableSet} view of the keys contained in this map. + * The set's iterator returns the keys in ascending order. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. The set supports element + * removal, which removes the corresponding mapping from the map, + * via the {@code Iterator.remove}, {@code Set.remove}, + * {@code removeAll}, {@code retainAll}, and {@code clear} + * operations. It does not support the {@code add} or {@code addAll} + * operations. + * + * <p>The view's {@code iterator} is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + * + * <p>This method is equivalent to method {@code navigableKeySet}. + * + * @return a navigable set view of the keys in this map + */ + NavigableSet<K> keySet(); + + /** + * Returns a reverse order {@link NavigableSet} view of the keys contained in this map. + * The set's iterator returns the keys in descending order. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. The set supports element + * removal, which removes the corresponding mapping from the map, + * via the {@code Iterator.remove}, {@code Set.remove}, + * {@code removeAll}, {@code retainAll}, and {@code clear} + * operations. It does not support the {@code add} or {@code addAll} + * operations. + * + * <p>The view's {@code iterator} is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + * + * @return a reverse order navigable set view of the keys in this map + */ + public NavigableSet<K> descendingKeySet(); +} diff --git a/concurrent/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java b/concurrent/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java new file mode 100644 index 0000000..d73d163 --- /dev/null +++ b/concurrent/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java @@ -0,0 +1,3115 @@ +/* + * 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; +import java.util.*; +import java.util.concurrent.atomic.*; + +/** + * A scalable concurrent {@link ConcurrentNavigableMap} implementation. + * The map is sorted according to the {@linkplain Comparable natural + * ordering} of its keys, or by a {@link Comparator} provided at map + * creation time, depending on which constructor is used. + * + * <p>This class implements a concurrent variant of <a + * href="http://www.cs.umd.edu/~pugh/">SkipLists</a> providing + * expected average <i>log(n)</i> time cost for the + * <tt>containsKey</tt>, <tt>get</tt>, <tt>put</tt> and + * <tt>remove</tt> operations and their variants. Insertion, removal, + * update, and access operations safely execute concurrently by + * multiple threads. Iterators are <i>weakly consistent</i>, returning + * elements reflecting the state of the map at some point at or since + * the creation of the iterator. They do <em>not</em> throw {@link + * ConcurrentModificationException}, and may proceed concurrently with + * other operations. Ascending key ordered views and their iterators + * are faster than descending ones. + * + * <p>All <tt>Map.Entry</tt> pairs returned by methods in this class + * and its views represent snapshots of mappings at the time they were + * produced. They do <em>not</em> support the <tt>Entry.setValue</tt> + * method. (Note however that it is possible to change mappings in the + * associated map using <tt>put</tt>, <tt>putIfAbsent</tt>, or + * <tt>replace</tt>, depending on exactly which effect you need.) + * + * <p>Beware that, unlike in most collections, the <tt>size</tt> + * method is <em>not</em> a constant-time operation. Because of the + * asynchronous nature of these maps, determining the current number + * of elements requires a traversal of the elements. Additionally, + * the bulk operations <tt>putAll</tt>, <tt>equals</tt>, and + * <tt>clear</tt> are <em>not</em> guaranteed to be performed + * atomically. For example, an iterator operating concurrently with a + * <tt>putAll</tt> operation might view only some of the added + * elements. + * + * <p>This class and its views and iterators implement all of the + * <em>optional</em> methods of the {@link Map} and {@link Iterator} + * interfaces. Like most other concurrent collections, this class does + * <em>not</em> permit the use of <tt>null</tt> keys or values because some + * null return values cannot be reliably distinguished from the absence of + * elements. + * + * <p>This class is a member of the + * <a href="{@docRoot}/../technotes/guides/collections/index.html"> + * Java Collections Framework</a>. + * + * @author Doug Lea + * @param <K> the type of keys maintained by this map + * @param <V> the type of mapped values + * @since 1.6 + */ +public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> + implements ConcurrentNavigableMap<K,V>, + Cloneable, + java.io.Serializable { + /* + * This class implements a tree-like two-dimensionally linked skip + * list in which the index levels are represented in separate + * nodes from the base nodes holding data. There are two reasons + * for taking this approach instead of the usual array-based + * structure: 1) Array based implementations seem to encounter + * more complexity and overhead 2) We can use cheaper algorithms + * for the heavily-traversed index lists than can be used for the + * base lists. Here's a picture of some of the basics for a + * possible list with 2 levels of index: + * + * Head nodes Index nodes + * +-+ right +-+ +-+ + * |2|---------------->| |--------------------->| |->null + * +-+ +-+ +-+ + * | down | | + * v v v + * +-+ +-+ +-+ +-+ +-+ +-+ + * |1|----------->| |->| |------>| |----------->| |------>| |->null + * +-+ +-+ +-+ +-+ +-+ +-+ + * v | | | | | + * Nodes next v v v v v + * +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ + * | |->|A|->|B|->|C|->|D|->|E|->|F|->|G|->|H|->|I|->|J|->|K|->null + * +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ + * + * The base lists use a variant of the HM linked ordered set + * algorithm. See Tim Harris, "A pragmatic implementation of + * non-blocking linked lists" + * http://www.cl.cam.ac.uk/~tlh20/publications.html and Maged + * Michael "High Performance Dynamic Lock-Free Hash Tables and + * List-Based Sets" + * http://www.research.ibm.com/people/m/michael/pubs.htm. The + * basic idea in these lists is to mark the "next" pointers of + * deleted nodes when deleting to avoid conflicts with concurrent + * insertions, and when traversing to keep track of triples + * (predecessor, node, successor) in order to detect when and how + * to unlink these deleted nodes. + * + * Rather than using mark-bits to mark list deletions (which can + * be slow and space-intensive using AtomicMarkedReference), nodes + * use direct CAS'able next pointers. On deletion, instead of + * marking a pointer, they splice in another node that can be + * thought of as standing for a marked pointer (indicating this by + * using otherwise impossible field values). Using plain nodes + * acts roughly like "boxed" implementations of marked pointers, + * but uses new nodes only when nodes are deleted, not for every + * link. This requires less space and supports faster + * traversal. Even if marked references were better supported by + * JVMs, traversal using this technique might still be faster + * because any search need only read ahead one more node than + * otherwise required (to check for trailing marker) rather than + * unmasking mark bits or whatever on each read. + * + * This approach maintains the essential property needed in the HM + * algorithm of changing the next-pointer of a deleted node so + * that any other CAS of it will fail, but implements the idea by + * changing the pointer to point to a different node, not by + * marking it. While it would be possible to further squeeze + * space by defining marker nodes not to have key/value fields, it + * isn't worth the extra type-testing overhead. The deletion + * markers are rarely encountered during traversal and are + * normally quickly garbage collected. (Note that this technique + * would not work well in systems without garbage collection.) + * + * In addition to using deletion markers, the lists also use + * nullness of value fields to indicate deletion, in a style + * similar to typical lazy-deletion schemes. If a node's value is + * null, then it is considered logically deleted and ignored even + * though it is still reachable. This maintains proper control of + * concurrent replace vs delete operations -- an attempted replace + * must fail if a delete beat it by nulling field, and a delete + * must return the last non-null value held in the field. (Note: + * Null, rather than some special marker, is used for value fields + * here because it just so happens to mesh with the Map API + * requirement that method get returns null if there is no + * mapping, which allows nodes to remain concurrently readable + * even when deleted. Using any other marker value here would be + * messy at best.) + * + * Here's the sequence of events for a deletion of node n with + * predecessor b and successor f, initially: + * + * +------+ +------+ +------+ + * ... | b |------>| n |----->| f | ... + * +------+ +------+ +------+ + * + * 1. CAS n's value field from non-null to null. + * From this point on, no public operations encountering + * the node consider this mapping to exist. However, other + * ongoing insertions and deletions might still modify + * n's next pointer. + * + * 2. CAS n's next pointer to point to a new marker node. + * From this point on, no other nodes can be appended to n. + * which avoids deletion errors in CAS-based linked lists. + * + * +------+ +------+ +------+ +------+ + * ... | b |------>| n |----->|marker|------>| f | ... + * +------+ +------+ +------+ +------+ + * + * 3. CAS b's next pointer over both n and its marker. + * From this point on, no new traversals will encounter n, + * and it can eventually be GCed. + * +------+ +------+ + * ... | b |----------------------------------->| f | ... + * +------+ +------+ + * + * A failure at step 1 leads to simple retry due to a lost race + * with another operation. Steps 2-3 can fail because some other + * thread noticed during a traversal a node with null value and + * helped out by marking and/or unlinking. This helping-out + * ensures that no thread can become stuck waiting for progress of + * the deleting thread. The use of marker nodes slightly + * complicates helping-out code because traversals must track + * consistent reads of up to four nodes (b, n, marker, f), not + * just (b, n, f), although the next field of a marker is + * immutable, and once a next field is CAS'ed to point to a + * marker, it never again changes, so this requires less care. + * + * Skip lists add indexing to this scheme, so that the base-level + * traversals start close to the locations being found, inserted + * or deleted -- usually base level traversals only traverse a few + * nodes. This doesn't change the basic algorithm except for the + * need to make sure base traversals start at predecessors (here, + * b) that are not (structurally) deleted, otherwise retrying + * after processing the deletion. + * + * Index levels are maintained as lists with volatile next fields, + * using CAS to link and unlink. Races are allowed in index-list + * operations that can (rarely) fail to link in a new index node + * or delete one. (We can't do this of course for data nodes.) + * However, even when this happens, the index lists remain sorted, + * so correctly serve as indices. This can impact performance, + * but since skip lists are probabilistic anyway, the net result + * is that under contention, the effective "p" value may be lower + * than its nominal value. And race windows are kept small enough + * that in practice these failures are rare, even under a lot of + * contention. + * + * The fact that retries (for both base and index lists) are + * relatively cheap due to indexing allows some minor + * simplifications of retry logic. Traversal restarts are + * performed after most "helping-out" CASes. This isn't always + * strictly necessary, but the implicit backoffs tend to help + * reduce other downstream failed CAS's enough to outweigh restart + * cost. This worsens the worst case, but seems to improve even + * highly contended cases. + * + * Unlike most skip-list implementations, index insertion and + * deletion here require a separate traversal pass occuring after + * the base-level action, to add or remove index nodes. This adds + * to single-threaded overhead, but improves contended + * multithreaded performance by narrowing interference windows, + * and allows deletion to ensure that all index nodes will be made + * unreachable upon return from a public remove operation, thus + * avoiding unwanted garbage retention. This is more important + * here than in some other data structures because we cannot null + * out node fields referencing user keys since they might still be + * read by other ongoing traversals. + * + * Indexing uses skip list parameters that maintain good search + * performance while using sparser-than-usual indices: The + * hardwired parameters k=1, p=0.5 (see method randomLevel) mean + * that about one-quarter of the nodes have indices. Of those that + * do, half have one level, a quarter have two, and so on (see + * Pugh's Skip List Cookbook, sec 3.4). The expected total space + * requirement for a map is slightly less than for the current + * implementation of java.util.TreeMap. + * + * Changing the level of the index (i.e, the height of the + * tree-like structure) also uses CAS. The head index has initial + * level/height of one. Creation of an index with height greater + * than the current level adds a level to the head index by + * CAS'ing on a new top-most head. To maintain good performance + * after a lot of removals, deletion methods heuristically try to + * reduce the height if the topmost levels appear to be empty. + * This may encounter races in which it possible (but rare) to + * reduce and "lose" a level just as it is about to contain an + * index (that will then never be encountered). This does no + * structural harm, and in practice appears to be a better option + * than allowing unrestrained growth of levels. + * + * The code for all this is more verbose than you'd like. Most + * operations entail locating an element (or position to insert an + * element). The code to do this can't be nicely factored out + * because subsequent uses require a snapshot of predecessor + * and/or successor and/or value fields which can't be returned + * all at once, at least not without creating yet another object + * to hold them -- creating such little objects is an especially + * bad idea for basic internal search operations because it adds + * to GC overhead. (This is one of the few times I've wished Java + * had macros.) Instead, some traversal code is interleaved within + * insertion and removal operations. The control logic to handle + * all the retry conditions is sometimes twisty. Most search is + * broken into 2 parts. findPredecessor() searches index nodes + * only, returning a base-level predecessor of the key. findNode() + * finishes out the base-level search. Even with this factoring, + * there is a fair amount of near-duplication of code to handle + * variants. + * + * For explanation of algorithms sharing at least a couple of + * features with this one, see Mikhail Fomitchev's thesis + * (http://www.cs.yorku.ca/~mikhail/), Keir Fraser's thesis + * (http://www.cl.cam.ac.uk/users/kaf24/), and Hakan Sundell's + * thesis (http://www.cs.chalmers.se/~phs/). + * + * Given the use of tree-like index nodes, you might wonder why + * this doesn't use some kind of search tree instead, which would + * support somewhat faster search operations. The reason is that + * there are no known efficient lock-free insertion and deletion + * algorithms for search trees. The immutability of the "down" + * links of index nodes (as opposed to mutable "left" fields in + * true trees) makes this tractable using only CAS operations. + * + * Notation guide for local variables + * Node: b, n, f for predecessor, node, successor + * Index: q, r, d for index node, right, down. + * t for another index node + * Head: h + * Levels: j + * Keys: k, key + * Values: v, value + * Comparisons: c + */ + + private static final long serialVersionUID = -8627078645895051609L; + + /** + * Generates the initial random seed for the cheaper per-instance + * random number generators used in randomLevel. + */ + private static final Random seedGenerator = new Random(); + + /** + * Special value used to identify base-level header + */ + private static final Object BASE_HEADER = new Object(); + + /** + * The topmost head index of the skiplist. + */ + private transient volatile HeadIndex<K,V> head; + + /** + * The comparator used to maintain order in this map, or null + * if using natural ordering. + * @serial + */ + private final Comparator<? super K> comparator; + + /** + * Seed for simple random number generator. Not volatile since it + * doesn't matter too much if different threads don't see updates. + */ + private transient int randomSeed; + + /** Lazily initialized key set */ + private transient KeySet keySet; + /** Lazily initialized entry set */ + private transient EntrySet entrySet; + /** Lazily initialized values collection */ + private transient Values values; + /** Lazily initialized descending key set */ + private transient ConcurrentNavigableMap<K,V> descendingMap; + + /** + * Initializes or resets state. Needed by constructors, clone, + * clear, readObject. and ConcurrentSkipListSet.clone. + * (Note that comparator must be separately initialized.) + */ + final void initialize() { + keySet = null; + entrySet = null; + values = null; + descendingMap = null; + randomSeed = seedGenerator.nextInt() | 0x0100; // ensure nonzero + head = new HeadIndex<K,V>(new Node<K,V>(null, BASE_HEADER, null), + null, null, 1); + } + + /** Updater for casHead */ + private static final + AtomicReferenceFieldUpdater<ConcurrentSkipListMap, HeadIndex> + headUpdater = AtomicReferenceFieldUpdater.newUpdater + (ConcurrentSkipListMap.class, HeadIndex.class, "head"); + + /** + * compareAndSet head node + */ + private boolean casHead(HeadIndex<K,V> cmp, HeadIndex<K,V> val) { + return headUpdater.compareAndSet(this, cmp, val); + } + + /* ---------------- Nodes -------------- */ + + /** + * Nodes hold keys and values, and are singly linked in sorted + * order, possibly with some intervening marker nodes. The list is + * headed by a dummy node accessible as head.node. The value field + * is declared only as Object because it takes special non-V + * values for marker and header nodes. + */ + static final class Node<K,V> { + final K key; + volatile Object value; + volatile Node<K,V> next; + + /** + * Creates a new regular node. + */ + Node(K key, Object value, Node<K,V> next) { + this.key = key; + this.value = value; + this.next = next; + } + + /** + * Creates a new marker node. A marker is distinguished by + * having its value field point to itself. Marker nodes also + * have null keys, a fact that is exploited in a few places, + * but this doesn't distinguish markers from the base-level + * header node (head.node), which also has a null key. + */ + Node(Node<K,V> next) { + this.key = null; + this.value = this; + this.next = next; + } + + /** Updater for casNext */ + static final AtomicReferenceFieldUpdater<Node, Node> + nextUpdater = AtomicReferenceFieldUpdater.newUpdater + (Node.class, Node.class, "next"); + + /** Updater for casValue */ + static final AtomicReferenceFieldUpdater<Node, Object> + valueUpdater = AtomicReferenceFieldUpdater.newUpdater + (Node.class, Object.class, "value"); + + /** + * compareAndSet value field + */ + boolean casValue(Object cmp, Object val) { + return valueUpdater.compareAndSet(this, cmp, val); + } + + /** + * compareAndSet next field + */ + boolean casNext(Node<K,V> cmp, Node<K,V> val) { + return nextUpdater.compareAndSet(this, cmp, val); + } + + /** + * Returns true if this node is a marker. This method isn't + * actually called in any current code checking for markers + * because callers will have already read value field and need + * to use that read (not another done here) and so directly + * test if value points to node. + * @param n a possibly null reference to a node + * @return true if this node is a marker node + */ + boolean isMarker() { + return value == this; + } + + /** + * Returns true if this node is the header of base-level list. + * @return true if this node is header node + */ + boolean isBaseHeader() { + return value == BASE_HEADER; + } + + /** + * Tries to append a deletion marker to this node. + * @param f the assumed current successor of this node + * @return true if successful + */ + boolean appendMarker(Node<K,V> f) { + return casNext(f, new Node<K,V>(f)); + } + + /** + * Helps out a deletion by appending marker or unlinking from + * predecessor. This is called during traversals when value + * field seen to be null. + * @param b predecessor + * @param f successor + */ + void helpDelete(Node<K,V> b, Node<K,V> f) { + /* + * Rechecking links and then doing only one of the + * help-out stages per call tends to minimize CAS + * interference among helping threads. + */ + if (f == next && this == b.next) { + if (f == null || f.value != f) // not already marked + appendMarker(f); + else + b.casNext(this, f.next); + } + } + + /** + * Returns value if this node contains a valid key-value pair, + * else null. + * @return this node's value if it isn't a marker or header or + * is deleted, else null. + */ + V getValidValue() { + Object v = value; + if (v == this || v == BASE_HEADER) + return null; + return (V)v; + } + + /** + * Creates and returns a new SimpleImmutableEntry holding current + * mapping if this node holds a valid value, else null. + * @return new entry or null + */ + AbstractMap.SimpleImmutableEntry<K,V> createSnapshot() { + V v = getValidValue(); + if (v == null) + return null; + return new AbstractMap.SimpleImmutableEntry<K,V>(key, v); + } + } + + /* ---------------- Indexing -------------- */ + + /** + * Index nodes represent the levels of the skip list. Note that + * even though both Nodes and Indexes have forward-pointing + * fields, they have different types and are handled in different + * ways, that can't nicely be captured by placing field in a + * shared abstract class. + */ + static class Index<K,V> { + final Node<K,V> node; + final Index<K,V> down; + volatile Index<K,V> right; + + /** + * Creates index node with given values. + */ + Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) { + this.node = node; + this.down = down; + this.right = right; + } + + /** Updater for casRight */ + static final AtomicReferenceFieldUpdater<Index, Index> + rightUpdater = AtomicReferenceFieldUpdater.newUpdater + (Index.class, Index.class, "right"); + + /** + * compareAndSet right field + */ + final boolean casRight(Index<K,V> cmp, Index<K,V> val) { + return rightUpdater.compareAndSet(this, cmp, val); + } + + /** + * Returns true if the node this indexes has been deleted. + * @return true if indexed node is known to be deleted + */ + final boolean indexesDeletedNode() { + return node.value == null; + } + + /** + * Tries to CAS newSucc as successor. To minimize races with + * unlink that may lose this index node, if the node being + * indexed is known to be deleted, it doesn't try to link in. + * @param succ the expected current successor + * @param newSucc the new successor + * @return true if successful + */ + final boolean link(Index<K,V> succ, Index<K,V> newSucc) { + Node<K,V> n = node; + newSucc.right = succ; + return n.value != null && casRight(succ, newSucc); + } + + /** + * Tries to CAS right field to skip over apparent successor + * succ. Fails (forcing a retraversal by caller) if this node + * is known to be deleted. + * @param succ the expected current successor + * @return true if successful + */ + final boolean unlink(Index<K,V> succ) { + return !indexesDeletedNode() && casRight(succ, succ.right); + } + } + + /* ---------------- Head nodes -------------- */ + + /** + * Nodes heading each level keep track of their level. + */ + static final class HeadIndex<K,V> extends Index<K,V> { + final int level; + HeadIndex(Node<K,V> node, Index<K,V> down, Index<K,V> right, int level) { + super(node, down, right); + this.level = level; + } + } + + /* ---------------- Comparison utilities -------------- */ + + /** + * Represents a key with a comparator as a Comparable. + * + * Because most sorted collections seem to use natural ordering on + * Comparables (Strings, Integers, etc), most internal methods are + * geared to use them. This is generally faster than checking + * per-comparison whether to use comparator or comparable because + * it doesn't require a (Comparable) cast for each comparison. + * (Optimizers can only sometimes remove such redundant checks + * themselves.) When Comparators are used, + * ComparableUsingComparators are created so that they act in the + * same way as natural orderings. This penalizes use of + * Comparators vs Comparables, which seems like the right + * tradeoff. + */ + static final class ComparableUsingComparator<K> implements Comparable<K> { + final K actualKey; + final Comparator<? super K> cmp; + ComparableUsingComparator(K key, Comparator<? super K> cmp) { + this.actualKey = key; + this.cmp = cmp; + } + public int compareTo(K k2) { + return cmp.compare(actualKey, k2); + } + } + + /** + * If using comparator, return a ComparableUsingComparator, else + * cast key as Comparable, which may cause ClassCastException, + * which is propagated back to caller. + */ + private Comparable<? super K> comparable(Object key) throws ClassCastException { + if (key == null) + throw new NullPointerException(); + if (comparator != null) + return new ComparableUsingComparator<K>((K)key, comparator); + else + return (Comparable<? super K>)key; + } + + /** + * Compares using comparator or natural ordering. Used when the + * ComparableUsingComparator approach doesn't apply. + */ + int compare(K k1, K k2) throws ClassCastException { + Comparator<? super K> cmp = comparator; + if (cmp != null) + return cmp.compare(k1, k2); + else + return ((Comparable<? super K>)k1).compareTo(k2); + } + + /** + * Returns true if given key greater than or equal to least and + * strictly less than fence, bypassing either test if least or + * fence are null. Needed mainly in submap operations. + */ + boolean inHalfOpenRange(K key, K least, K fence) { + if (key == null) + throw new NullPointerException(); + return ((least == null || compare(key, least) >= 0) && + (fence == null || compare(key, fence) < 0)); + } + + /** + * Returns true if given key greater than or equal to least and less + * or equal to fence. Needed mainly in submap operations. + */ + boolean inOpenRange(K key, K least, K fence) { + if (key == null) + throw new NullPointerException(); + return ((least == null || compare(key, least) >= 0) && + (fence == null || compare(key, fence) <= 0)); + } + + /* ---------------- Traversal -------------- */ + + /** + * Returns a base-level node with key strictly less than given key, + * or the base-level header if there is no such node. Also + * unlinks indexes to deleted nodes found along the way. Callers + * rely on this side-effect of clearing indices to deleted nodes. + * @param key the key + * @return a predecessor of key + */ + private Node<K,V> findPredecessor(Comparable<? super K> key) { + if (key == null) + throw new NullPointerException(); // don't postpone errors + for (;;) { + Index<K,V> q = head; + Index<K,V> r = q.right; + for (;;) { + if (r != null) { + Node<K,V> n = r.node; + K k = n.key; + if (n.value == null) { + if (!q.unlink(r)) + break; // restart + r = q.right; // reread r + continue; + } + if (key.compareTo(k) > 0) { + q = r; + r = r.right; + continue; + } + } + Index<K,V> d = q.down; + if (d != null) { + q = d; + r = d.right; + } else + return q.node; + } + } + } + + /** + * Returns node holding key or null if no such, clearing out any + * deleted nodes seen along the way. Repeatedly traverses at + * base-level looking for key starting at predecessor returned + * from findPredecessor, processing base-level deletions as + * encountered. Some callers rely on this side-effect of clearing + * deleted nodes. + * + * Restarts occur, at traversal step centered on node n, if: + * + * (1) After reading n's next field, n is no longer assumed + * predecessor b's current successor, which means that + * we don't have a consistent 3-node snapshot and so cannot + * unlink any subsequent deleted nodes encountered. + * + * (2) n's value field is null, indicating n is deleted, in + * which case we help out an ongoing structural deletion + * before retrying. Even though there are cases where such + * unlinking doesn't require restart, they aren't sorted out + * here because doing so would not usually outweigh cost of + * restarting. + * + * (3) n is a marker or n's predecessor's value field is null, + * indicating (among other possibilities) that + * findPredecessor returned a deleted node. We can't unlink + * the node because we don't know its predecessor, so rely + * on another call to findPredecessor to notice and return + * some earlier predecessor, which it will do. This check is + * only strictly needed at beginning of loop, (and the + * b.value check isn't strictly needed at all) but is done + * each iteration to help avoid contention with other + * threads by callers that will fail to be able to change + * links, and so will retry anyway. + * + * The traversal loops in doPut, doRemove, and findNear all + * include the same three kinds of checks. And specialized + * versions appear in findFirst, and findLast and their + * variants. They can't easily share code because each uses the + * reads of fields held in locals occurring in the orders they + * were performed. + * + * @param key the key + * @return node holding key, or null if no such + */ + private Node<K,V> findNode(Comparable<? super K> key) { + for (;;) { + Node<K,V> b = findPredecessor(key); + Node<K,V> n = b.next; + for (;;) { + if (n == null) + return null; + Node<K,V> f = n.next; + if (n != b.next) // inconsistent read + break; + Object v = n.value; + if (v == null) { // n is deleted + n.helpDelete(b, f); + break; + } + if (v == n || b.value == null) // b is deleted + break; + int c = key.compareTo(n.key); + if (c == 0) + return n; + if (c < 0) + return null; + b = n; + n = f; + } + } + } + + /** + * Specialized variant of findNode to perform Map.get. Does a weak + * traversal, not bothering to fix any deleted index nodes, + * returning early if it happens to see key in index, and passing + * over any deleted base nodes, falling back to getUsingFindNode + * only if it would otherwise return value from an ongoing + * deletion. Also uses "bound" to eliminate need for some + * comparisons (see Pugh Cookbook). Also folds uses of null checks + * and node-skipping because markers have null keys. + * @param okey the key + * @return the value, or null if absent + */ + private V doGet(Object okey) { + Comparable<? super K> key = comparable(okey); + Node<K,V> bound = null; + Index<K,V> q = head; + Index<K,V> r = q.right; + Node<K,V> n; + K k; + int c; + for (;;) { + Index<K,V> d; + // Traverse rights + if (r != null && (n = r.node) != bound && (k = n.key) != null) { + if ((c = key.compareTo(k)) > 0) { + q = r; + r = r.right; + continue; + } else if (c == 0) { + Object v = n.value; + return (v != null)? (V)v : getUsingFindNode(key); + } else + bound = n; + } + + // Traverse down + if ((d = q.down) != null) { + q = d; + r = d.right; + } else + break; + } + + // Traverse nexts + for (n = q.node.next; n != null; n = n.next) { + if ((k = n.key) != null) { + if ((c = key.compareTo(k)) == 0) { + Object v = n.value; + return (v != null)? (V)v : getUsingFindNode(key); + } else if (c < 0) + break; + } + } + return null; + } + + /** + * Performs map.get via findNode. Used as a backup if doGet + * encounters an in-progress deletion. + * @param key the key + * @return the value, or null if absent + */ + private V getUsingFindNode(Comparable<? super K> key) { + /* + * Loop needed here and elsewhere in case value field goes + * null just as it is about to be returned, in which case we + * lost a race with a deletion, so must retry. + */ + for (;;) { + Node<K,V> n = findNode(key); + if (n == null) + return null; + Object v = n.value; + if (v != null) + return (V)v; + } + } + + /* ---------------- Insertion -------------- */ + + /** + * Main insertion method. Adds element if not present, or + * replaces value if present and onlyIfAbsent is false. + * @param kkey the key + * @param value the value that must be associated with key + * @param onlyIfAbsent if should not insert if already present + * @return the old value, or null if newly inserted + */ + private V doPut(K kkey, V value, boolean onlyIfAbsent) { + Comparable<? super K> key = comparable(kkey); + for (;;) { + Node<K,V> b = findPredecessor(key); + Node<K,V> n = b.next; + for (;;) { + if (n != null) { + Node<K,V> f = n.next; + if (n != b.next) // inconsistent read + break; + Object v = n.value; + if (v == null) { // n is deleted + n.helpDelete(b, f); + break; + } + if (v == n || b.value == null) // b is deleted + break; + int c = key.compareTo(n.key); + if (c > 0) { + b = n; + n = f; + continue; + } + if (c == 0) { + if (onlyIfAbsent || n.casValue(v, value)) + return (V)v; + else + break; // restart if lost race to replace value + } + // else c < 0; fall through + } + + Node<K,V> z = new Node<K,V>(kkey, value, n); + if (!b.casNext(n, z)) + break; // restart if lost race to append to b + int level = randomLevel(); + if (level > 0) + insertIndex(z, level); + return null; + } + } + } + + /** + * Returns a random level for inserting a new node. + * Hardwired to k=1, p=0.5, max 31 (see above and + * Pugh's "Skip List Cookbook", sec 3.4). + * + * This uses the simplest of the generators described in George + * Marsaglia's "Xorshift RNGs" paper. This is not a high-quality + * generator but is acceptable here. + */ + private int randomLevel() { + int x = randomSeed; + x ^= x << 13; + x ^= x >>> 17; + randomSeed = x ^= x << 5; + if ((x & 0x8001) != 0) // test highest and lowest bits + return 0; + int level = 1; + while (((x >>>= 1) & 1) != 0) ++level; + return level; + } + + /** + * Creates and adds index nodes for the given node. + * @param z the node + * @param level the level of the index + */ + private void insertIndex(Node<K,V> z, int level) { + HeadIndex<K,V> h = head; + int max = h.level; + + if (level <= max) { + Index<K,V> idx = null; + for (int i = 1; i <= level; ++i) + idx = new Index<K,V>(z, idx, null); + addIndex(idx, h, level); + + } else { // Add a new level + /* + * To reduce interference by other threads checking for + * empty levels in tryReduceLevel, new levels are added + * with initialized right pointers. Which in turn requires + * keeping levels in an array to access them while + * creating new head index nodes from the opposite + * direction. + */ + level = max + 1; + Index<K,V>[] idxs = (Index<K,V>[])new Index[level+1]; + Index<K,V> idx = null; + for (int i = 1; i <= level; ++i) + idxs[i] = idx = new Index<K,V>(z, idx, null); + + HeadIndex<K,V> oldh; + int k; + for (;;) { + oldh = head; + int oldLevel = oldh.level; + if (level <= oldLevel) { // lost race to add level + k = level; + break; + } + HeadIndex<K,V> newh = oldh; + Node<K,V> oldbase = oldh.node; + for (int j = oldLevel+1; j <= level; ++j) + newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j); + if (casHead(oldh, newh)) { + k = oldLevel; + break; + } + } + addIndex(idxs[k], oldh, k); + } + } + + /** + * Adds given index nodes from given level down to 1. + * @param idx the topmost index node being inserted + * @param h the value of head to use to insert. This must be + * snapshotted by callers to provide correct insertion level + * @param indexLevel the level of the index + */ + private void addIndex(Index<K,V> idx, HeadIndex<K,V> h, int indexLevel) { + // Track next level to insert in case of retries + int insertionLevel = indexLevel; + Comparable<? super K> key = comparable(idx.node.key); + if (key == null) throw new NullPointerException(); + + // Similar to findPredecessor, but adding index nodes along + // path to key. + for (;;) { + int j = h.level; + Index<K,V> q = h; + Index<K,V> r = q.right; + Index<K,V> t = idx; + for (;;) { + if (r != null) { + Node<K,V> n = r.node; + // compare before deletion check avoids needing recheck + int c = key.compareTo(n.key); + if (n.value == null) { + if (!q.unlink(r)) + break; + r = q.right; + continue; + } + if (c > 0) { + q = r; + r = r.right; + continue; + } + } + + if (j == insertionLevel) { + // Don't insert index if node already deleted + if (t.indexesDeletedNode()) { + findNode(key); // cleans up + return; + } + if (!q.link(r, t)) + break; // restart + if (--insertionLevel == 0) { + // need final deletion check before return + if (t.indexesDeletedNode()) + findNode(key); + return; + } + } + + if (--j >= insertionLevel && j < indexLevel) + t = t.down; + q = q.down; + r = q.right; + } + } + } + + /* ---------------- Deletion -------------- */ + + /** + * Main deletion method. Locates node, nulls value, appends a + * deletion marker, unlinks predecessor, removes associated index + * nodes, and possibly reduces head index level. + * + * Index nodes are cleared out simply by calling findPredecessor. + * which unlinks indexes to deleted nodes found along path to key, + * which will include the indexes to this node. This is done + * unconditionally. We can't check beforehand whether there are + * index nodes because it might be the case that some or all + * indexes hadn't been inserted yet for this node during initial + * search for it, and we'd like to ensure lack of garbage + * retention, so must call to be sure. + * + * @param okey the key + * @param value if non-null, the value that must be + * associated with key + * @return the node, or null if not found + */ + final V doRemove(Object okey, Object value) { + Comparable<? super K> key = comparable(okey); + for (;;) { + Node<K,V> b = findPredecessor(key); + Node<K,V> n = b.next; + for (;;) { + if (n == null) + return null; + Node<K,V> f = n.next; + if (n != b.next) // inconsistent read + break; + Object v = n.value; + if (v == null) { // n is deleted + n.helpDelete(b, f); + break; + } + if (v == n || b.value == null) // b is deleted + break; + int c = key.compareTo(n.key); + if (c < 0) + return null; + if (c > 0) { + b = n; + n = f; + continue; + } + if (value != null && !value.equals(v)) + return null; + if (!n.casValue(v, null)) + break; + if (!n.appendMarker(f) || !b.casNext(n, f)) + findNode(key); // Retry via findNode + else { + findPredecessor(key); // Clean index + if (head.right == null) + tryReduceLevel(); + } + return (V)v; + } + } + } + + /** + * Possibly reduce head level if it has no nodes. This method can + * (rarely) make mistakes, in which case levels can disappear even + * though they are about to contain index nodes. This impacts + * performance, not correctness. To minimize mistakes as well as + * to reduce hysteresis, the level is reduced by one only if the + * topmost three levels look empty. Also, if the removed level + * looks non-empty after CAS, we try to change it back quick + * before anyone notices our mistake! (This trick works pretty + * well because this method will practically never make mistakes + * unless current thread stalls immediately before first CAS, in + * which case it is very unlikely to stall again immediately + * afterwards, so will recover.) + * + * We put up with all this rather than just let levels grow + * because otherwise, even a small map that has undergone a large + * number of insertions and removals will have a lot of levels, + * slowing down access more than would an occasional unwanted + * reduction. + */ + private void tryReduceLevel() { + HeadIndex<K,V> h = head; + HeadIndex<K,V> d; + HeadIndex<K,V> e; + if (h.level > 3 && + (d = (HeadIndex<K,V>)h.down) != null && + (e = (HeadIndex<K,V>)d.down) != null && + e.right == null && + d.right == null && + h.right == null && + casHead(h, d) && // try to set + h.right != null) // recheck + casHead(d, h); // try to backout + } + + /* ---------------- Finding and removing first element -------------- */ + + /** + * Specialized variant of findNode to get first valid node. + * @return first node or null if empty + */ + Node<K,V> findFirst() { + for (;;) { + Node<K,V> b = head.node; + Node<K,V> n = b.next; + if (n == null) + return null; + if (n.value != null) + return n; + n.helpDelete(b, n.next); + } + } + + /** + * Removes first entry; returns its snapshot. + * @return null if empty, else snapshot of first entry + */ + Map.Entry<K,V> doRemoveFirstEntry() { + for (;;) { + Node<K,V> b = head.node; + Node<K,V> n = b.next; + if (n == null) + return null; + Node<K,V> f = n.next; + if (n != b.next) + continue; + Object v = n.value; + if (v == null) { + n.helpDelete(b, f); + continue; + } + if (!n.casValue(v, null)) + continue; + if (!n.appendMarker(f) || !b.casNext(n, f)) + findFirst(); // retry + clearIndexToFirst(); + return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, (V)v); + } + } + + /** + * Clears out index nodes associated with deleted first entry. + */ + private void clearIndexToFirst() { + for (;;) { + Index<K,V> q = head; + for (;;) { + Index<K,V> r = q.right; + if (r != null && r.indexesDeletedNode() && !q.unlink(r)) + break; + if ((q = q.down) == null) { + if (head.right == null) + tryReduceLevel(); + return; + } + } + } + } + + + /* ---------------- Finding and removing last element -------------- */ + + /** + * Specialized version of find to get last valid node. + * @return last node or null if empty + */ + Node<K,V> findLast() { + /* + * findPredecessor can't be used to traverse index level + * because this doesn't use comparisons. So traversals of + * both levels are folded together. + */ + Index<K,V> q = head; + for (;;) { + Index<K,V> d, r; + if ((r = q.right) != null) { + if (r.indexesDeletedNode()) { + q.unlink(r); + q = head; // restart + } + else + q = r; + } else if ((d = q.down) != null) { + q = d; + } else { + Node<K,V> b = q.node; + Node<K,V> n = b.next; + for (;;) { + if (n == null) + return (b.isBaseHeader())? null : b; + Node<K,V> f = n.next; // inconsistent read + if (n != b.next) + break; + Object v = n.value; + if (v == null) { // n is deleted + n.helpDelete(b, f); + break; + } + if (v == n || b.value == null) // b is deleted + break; + b = n; + n = f; + } + q = head; // restart + } + } + } + + /** + * Specialized variant of findPredecessor to get predecessor of last + * valid node. Needed when removing the last entry. It is possible + * that all successors of returned node will have been deleted upon + * return, in which case this method can be retried. + * @return likely predecessor of last node + */ + private Node<K,V> findPredecessorOfLast() { + for (;;) { + Index<K,V> q = head; + for (;;) { + Index<K,V> d, r; + if ((r = q.right) != null) { + if (r.indexesDeletedNode()) { + q.unlink(r); + break; // must restart + } + // proceed as far across as possible without overshooting + if (r.node.next != null) { + q = r; + continue; + } + } + if ((d = q.down) != null) + q = d; + else + return q.node; + } + } + } + + /** + * Removes last entry; returns its snapshot. + * Specialized variant of doRemove. + * @return null if empty, else snapshot of last entry + */ + Map.Entry<K,V> doRemoveLastEntry() { + for (;;) { + Node<K,V> b = findPredecessorOfLast(); + Node<K,V> n = b.next; + if (n == null) { + if (b.isBaseHeader()) // empty + return null; + else + continue; // all b's successors are deleted; retry + } + for (;;) { + Node<K,V> f = n.next; + if (n != b.next) // inconsistent read + break; + Object v = n.value; + if (v == null) { // n is deleted + n.helpDelete(b, f); + break; + } + if (v == n || b.value == null) // b is deleted + break; + if (f != null) { + b = n; + n = f; + continue; + } + if (!n.casValue(v, null)) + break; + K key = n.key; + Comparable<? super K> ck = comparable(key); + if (!n.appendMarker(f) || !b.casNext(n, f)) + findNode(ck); // Retry via findNode + else { + findPredecessor(ck); // Clean index + if (head.right == null) + tryReduceLevel(); + } + return new AbstractMap.SimpleImmutableEntry<K,V>(key, (V)v); + } + } + } + + /* ---------------- Relational operations -------------- */ + + // Control values OR'ed as arguments to findNear + + private static final int EQ = 1; + private static final int LT = 2; + private static final int GT = 0; // Actually checked as !LT + + /** + * Utility for ceiling, floor, lower, higher methods. + * @param kkey the key + * @param rel the relation -- OR'ed combination of EQ, LT, GT + * @return nearest node fitting relation, or null if no such + */ + Node<K,V> findNear(K kkey, int rel) { + Comparable<? super K> key = comparable(kkey); + for (;;) { + Node<K,V> b = findPredecessor(key); + Node<K,V> n = b.next; + for (;;) { + if (n == null) + return ((rel & LT) == 0 || b.isBaseHeader())? null : b; + Node<K,V> f = n.next; + if (n != b.next) // inconsistent read + break; + Object v = n.value; + if (v == null) { // n is deleted + n.helpDelete(b, f); + break; + } + if (v == n || b.value == null) // b is deleted + break; + int c = key.compareTo(n.key); + if ((c == 0 && (rel & EQ) != 0) || + (c < 0 && (rel & LT) == 0)) + return n; + if ( c <= 0 && (rel & LT) != 0) + return (b.isBaseHeader())? null : b; + b = n; + n = f; + } + } + } + + /** + * Returns SimpleImmutableEntry for results of findNear. + * @param key the key + * @param rel the relation -- OR'ed combination of EQ, LT, GT + * @return Entry fitting relation, or null if no such + */ + AbstractMap.SimpleImmutableEntry<K,V> getNear(K key, int rel) { + for (;;) { + Node<K,V> n = findNear(key, rel); + if (n == null) + return null; + AbstractMap.SimpleImmutableEntry<K,V> e = n.createSnapshot(); + if (e != null) + return e; + } + } + + + /* ---------------- Constructors -------------- */ + + /** + * Constructs a new, empty map, sorted according to the + * {@linkplain Comparable natural ordering} of the keys. + */ + public ConcurrentSkipListMap() { + this.comparator = null; + initialize(); + } + + /** + * Constructs a new, empty map, sorted according to the specified + * comparator. + * + * @param comparator the comparator that will be used to order this map. + * If <tt>null</tt>, the {@linkplain Comparable natural + * ordering} of the keys will be used. + */ + public ConcurrentSkipListMap(Comparator<? super K> comparator) { + this.comparator = comparator; + initialize(); + } + + /** + * Constructs a new map containing the same mappings as the given map, + * sorted according to the {@linkplain Comparable natural ordering} of + * the keys. + * + * @param m the map whose mappings are to be placed in this map + * @throws ClassCastException if the keys in <tt>m</tt> are not + * {@link Comparable}, or are not mutually comparable + * @throws NullPointerException if the specified map or any of its keys + * or values are null + */ + public ConcurrentSkipListMap(Map<? extends K, ? extends V> m) { + this.comparator = null; + initialize(); + putAll(m); + } + + /** + * Constructs a new map containing the same mappings and using the + * same ordering as the specified sorted map. + * + * @param m the sorted map whose mappings are to be placed in this + * map, and whose comparator is to be used to sort this map + * @throws NullPointerException if the specified sorted map or any of + * its keys or values are null + */ + public ConcurrentSkipListMap(SortedMap<K, ? extends V> m) { + this.comparator = m.comparator(); + initialize(); + buildFromSorted(m); + } + + /** + * Returns a shallow copy of this <tt>ConcurrentSkipListMap</tt> + * instance. (The keys and values themselves are not cloned.) + * + * @return a shallow copy of this map + */ + public ConcurrentSkipListMap<K,V> clone() { + ConcurrentSkipListMap<K,V> clone = null; + try { + clone = (ConcurrentSkipListMap<K,V>) super.clone(); + } catch (CloneNotSupportedException e) { + throw new InternalError(); + } + + clone.initialize(); + clone.buildFromSorted(this); + return clone; + } + + /** + * Streamlined bulk insertion to initialize from elements of + * given sorted map. Call only from constructor or clone + * method. + */ + private void buildFromSorted(SortedMap<K, ? extends V> map) { + if (map == null) + throw new NullPointerException(); + + HeadIndex<K,V> h = head; + Node<K,V> basepred = h.node; + + // Track the current rightmost node at each level. Uses an + // ArrayList to avoid committing to initial or maximum level. + ArrayList<Index<K,V>> preds = new ArrayList<Index<K,V>>(); + + // initialize + for (int i = 0; i <= h.level; ++i) + preds.add(null); + Index<K,V> q = h; + for (int i = h.level; i > 0; --i) { + preds.set(i, q); + q = q.down; + } + + Iterator<? extends Map.Entry<? extends K, ? extends V>> it = + map.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<? extends K, ? extends V> e = it.next(); + int j = randomLevel(); + if (j > h.level) j = h.level + 1; + K k = e.getKey(); + V v = e.getValue(); + if (k == null || v == null) + throw new NullPointerException(); + Node<K,V> z = new Node<K,V>(k, v, null); + basepred.next = z; + basepred = z; + if (j > 0) { + Index<K,V> idx = null; + for (int i = 1; i <= j; ++i) { + idx = new Index<K,V>(z, idx, null); + if (i > h.level) + h = new HeadIndex<K,V>(h.node, h, idx, i); + + if (i < preds.size()) { + preds.get(i).right = idx; + preds.set(i, idx); + } else + preds.add(idx); + } + } + } + head = h; + } + + /* ---------------- Serialization -------------- */ + + /** + * Save the state of this map to a stream. + * + * @serialData The key (Object) and value (Object) for each + * key-value mapping represented by the map, followed by + * <tt>null</tt>. The key-value mappings are emitted in key-order + * (as determined by the Comparator, or by the keys' natural + * ordering if no Comparator). + */ + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + // Write out the Comparator and any hidden stuff + s.defaultWriteObject(); + + // Write out keys and values (alternating) + for (Node<K,V> n = findFirst(); n != null; n = n.next) { + V v = n.getValidValue(); + if (v != null) { + s.writeObject(n.key); + s.writeObject(v); + } + } + s.writeObject(null); + } + + /** + * Reconstitute the map from a stream. + */ + private void readObject(final java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + // Read in the Comparator and any hidden stuff + s.defaultReadObject(); + // Reset transients + initialize(); + + /* + * This is nearly identical to buildFromSorted, but is + * distinct because readObject calls can't be nicely adapted + * as the kind of iterator needed by buildFromSorted. (They + * can be, but doing so requires type cheats and/or creation + * of adaptor classes.) It is simpler to just adapt the code. + */ + + HeadIndex<K,V> h = head; + Node<K,V> basepred = h.node; + ArrayList<Index<K,V>> preds = new ArrayList<Index<K,V>>(); + for (int i = 0; i <= h.level; ++i) + preds.add(null); + Index<K,V> q = h; + for (int i = h.level; i > 0; --i) { + preds.set(i, q); + q = q.down; + } + + for (;;) { + Object k = s.readObject(); + if (k == null) + break; + Object v = s.readObject(); + if (v == null) + throw new NullPointerException(); + K key = (K) k; + V val = (V) v; + int j = randomLevel(); + if (j > h.level) j = h.level + 1; + Node<K,V> z = new Node<K,V>(key, val, null); + basepred.next = z; + basepred = z; + if (j > 0) { + Index<K,V> idx = null; + for (int i = 1; i <= j; ++i) { + idx = new Index<K,V>(z, idx, null); + if (i > h.level) + h = new HeadIndex<K,V>(h.node, h, idx, i); + + if (i < preds.size()) { + preds.get(i).right = idx; + preds.set(i, idx); + } else + preds.add(idx); + } + } + } + head = h; + } + + /* ------ Map API methods ------ */ + + /** + * Returns <tt>true</tt> if this map contains a mapping for the specified + * key. + * + * @param key key whose presence in this map is to be tested + * @return <tt>true</tt> if this map contains a mapping for the specified key + * @throws ClassCastException if the specified key cannot be compared + * with the keys currently in the map + * @throws NullPointerException if the specified key is null + */ + public boolean containsKey(Object key) { + return doGet(key) != null; + } + + /** + * 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} compares + * equal to {@code k} according to the map's ordering, then this + * method returns {@code v}; otherwise it returns {@code null}. + * (There can be at most one such mapping.) + * + * @throws ClassCastException if the specified key cannot be compared + * with the keys currently in the map + * @throws NullPointerException if the specified key is null + */ + public V get(Object key) { + return doGet(key); + } + + /** + * Associates the specified value with the specified key in this map. + * If the map previously contained a mapping for the key, the old + * value is replaced. + * + * @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 + * @throws ClassCastException if the specified key cannot be compared + * with the keys currently in the map + * @throws NullPointerException if the specified key or value is null + */ + public V put(K key, V value) { + if (value == null) + throw new NullPointerException(); + return doPut(key, value, false); + } + + /** + * Removes the mapping for the specified key from this map if present. + * + * @param key key for which mapping should be removed + * @return the previous value associated with the specified key, or + * <tt>null</tt> if there was no mapping for the key + * @throws ClassCastException if the specified key cannot be compared + * with the keys currently in the map + * @throws NullPointerException if the specified key is null + */ + public V remove(Object key) { + return doRemove(key, null); + } + + /** + * Returns <tt>true</tt> if this map maps one or more keys to the + * specified value. This operation requires time linear in the + * map size. + * + * @param value value whose presence in this map is to be tested + * @return <tt>true</tt> if a mapping to <tt>value</tt> exists; + * <tt>false</tt> otherwise + * @throws NullPointerException if the specified value is null + */ + public boolean containsValue(Object value) { + if (value == null) + throw new NullPointerException(); + for (Node<K,V> n = findFirst(); n != null; n = n.next) { + V v = n.getValidValue(); + if (v != null && value.equals(v)) + return true; + } + return false; + } + + /** + * Returns the number of key-value mappings in this map. If this map + * contains more than <tt>Integer.MAX_VALUE</tt> elements, it + * returns <tt>Integer.MAX_VALUE</tt>. + * + * <p>Beware that, unlike in most collections, this method is + * <em>NOT</em> a constant-time operation. Because of the + * asynchronous nature of these maps, determining the current + * number of elements requires traversing them all to count them. + * Additionally, it is possible for the size to change during + * execution of this method, in which case the returned result + * will be inaccurate. Thus, this method is typically not very + * useful in concurrent applications. + * + * @return the number of elements in this map + */ + public int size() { + long count = 0; + for (Node<K,V> n = findFirst(); n != null; n = n.next) { + if (n.getValidValue() != null) + ++count; + } + return (count >= Integer.MAX_VALUE)? Integer.MAX_VALUE : (int)count; + } + + /** + * 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() { + return findFirst() == null; + } + + /** + * Removes all of the mappings from this map. + */ + public void clear() { + initialize(); + } + + /* ---------------- View methods -------------- */ + + /* + * Note: Lazy initialization works for views because view classes + * are stateless/immutable so it doesn't matter wrt correctness if + * more than one is created (which will only rarely happen). Even + * so, the following idiom conservatively ensures that the method + * returns the one it created if it does so, not one created by + * another racing thread. + */ + + /** + * Returns a {@link NavigableSet} view of the keys contained in this map. + * The set's iterator returns the keys in ascending order. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. The set supports element + * removal, which removes the corresponding mapping from the map, + * via the {@code Iterator.remove}, {@code Set.remove}, + * {@code removeAll}, {@code retainAll}, and {@code clear} + * operations. It does not support the {@code add} or {@code addAll} + * operations. + * + * <p>The view's {@code iterator} is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + * + * <p>This method is equivalent to method {@code navigableKeySet}. + * + * @return a navigable set view of the keys in this map + */ + public NavigableSet<K> keySet() { + KeySet ks = keySet; + return (ks != null) ? ks : (keySet = new KeySet(this)); + } + + public NavigableSet<K> navigableKeySet() { + KeySet ks = keySet; + return (ks != null) ? ks : (keySet = new KeySet(this)); + } + + /** + * Returns a {@link Collection} view of the values contained in this map. + * The collection's iterator returns the values in ascending order + * of the corresponding keys. + * 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. + * + * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + */ + public Collection<V> values() { + Values vs = values; + return (vs != null) ? vs : (values = new Values(this)); + } + + /** + * Returns a {@link Set} view of the mappings contained in this map. + * The set's iterator returns the entries in ascending key order. + * 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. + * + * <p>The <tt>Map.Entry</tt> elements returned by + * <tt>iterator.next()</tt> do <em>not</em> support the + * <tt>setValue</tt> operation. + * + * @return a set view of the mappings contained in this map, + * sorted in ascending key order + */ + public Set<Map.Entry<K,V>> entrySet() { + EntrySet es = entrySet; + return (es != null) ? es : (entrySet = new EntrySet(this)); + } + + public ConcurrentNavigableMap<K,V> descendingMap() { + ConcurrentNavigableMap<K,V> dm = descendingMap; + return (dm != null) ? dm : (descendingMap = new SubMap<K,V> + (this, null, false, null, false, true)); + } + + public NavigableSet<K> descendingKeySet() { + return descendingMap().navigableKeySet(); + } + + /* ---------------- AbstractMap Overrides -------------- */ + + /** + * Compares the specified object with this map for equality. + * Returns <tt>true</tt> if the given object is also a map and the + * two maps represent the same mappings. More formally, two maps + * <tt>m1</tt> and <tt>m2</tt> represent the same mappings if + * <tt>m1.entrySet().equals(m2.entrySet())</tt>. This + * operation may return misleading results if either map is + * concurrently modified during execution of this method. + * + * @param o object to be compared for equality with this map + * @return <tt>true</tt> if the specified object is equal to this map + */ + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof Map)) + return false; + Map<?,?> m = (Map<?,?>) o; + try { + for (Map.Entry<K,V> e : this.entrySet()) + if (! e.getValue().equals(m.get(e.getKey()))) + return false; + for (Map.Entry<?,?> e : m.entrySet()) { + Object k = e.getKey(); + Object v = e.getValue(); + if (k == null || v == null || !v.equals(get(k))) + return false; + } + return true; + } catch (ClassCastException unused) { + return false; + } catch (NullPointerException unused) { + return false; + } + } + + /* ------ ConcurrentMap API methods ------ */ + + /** + * {@inheritDoc} + * + * @return the previous value associated with the specified key, + * or <tt>null</tt> if there was no mapping for the key + * @throws ClassCastException if the specified key cannot be compared + * with the keys currently in the map + * @throws NullPointerException if the specified key or value is null + */ + public V putIfAbsent(K key, V value) { + if (value == null) + throw new NullPointerException(); + return doPut(key, value, true); + } + + /** + * {@inheritDoc} + * + * @throws ClassCastException if the specified key cannot be compared + * with the keys currently in the map + * @throws NullPointerException if the specified key is null + */ + public boolean remove(Object key, Object value) { + if (key == null) + throw new NullPointerException(); + if (value == null) + return false; + return doRemove(key, value) != null; + } + + /** + * {@inheritDoc} + * + * @throws ClassCastException if the specified key cannot be compared + * with the keys currently in the map + * @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(); + Comparable<? super K> k = comparable(key); + for (;;) { + Node<K,V> n = findNode(k); + if (n == null) + return false; + Object v = n.value; + if (v != null) { + if (!oldValue.equals(v)) + return false; + if (n.casValue(v, newValue)) + return true; + } + } + } + + /** + * {@inheritDoc} + * + * @return the previous value associated with the specified key, + * or <tt>null</tt> if there was no mapping for the key + * @throws ClassCastException if the specified key cannot be compared + * with the keys currently in the map + * @throws NullPointerException if the specified key or value is null + */ + public V replace(K key, V value) { + if (value == null) + throw new NullPointerException(); + Comparable<? super K> k = comparable(key); + for (;;) { + Node<K,V> n = findNode(k); + if (n == null) + return null; + Object v = n.value; + if (v != null && n.casValue(v, value)) + return (V)v; + } + } + + /* ------ SortedMap API methods ------ */ + + public Comparator<? super K> comparator() { + return comparator; + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public K firstKey() { + Node<K,V> n = findFirst(); + if (n == null) + throw new NoSuchElementException(); + return n.key; + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public K lastKey() { + Node<K,V> n = findLast(); + if (n == null) + throw new NoSuchElementException(); + return n.key; + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if {@code fromKey} or {@code toKey} is null + * @throws IllegalArgumentException {@inheritDoc} + */ + public ConcurrentNavigableMap<K,V> subMap(K fromKey, + boolean fromInclusive, + K toKey, + boolean toInclusive) { + if (fromKey == null || toKey == null) + throw new NullPointerException(); + return new SubMap<K,V> + (this, fromKey, fromInclusive, toKey, toInclusive, false); + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if {@code toKey} is null + * @throws IllegalArgumentException {@inheritDoc} + */ + public ConcurrentNavigableMap<K,V> headMap(K toKey, + boolean inclusive) { + if (toKey == null) + throw new NullPointerException(); + return new SubMap<K,V> + (this, null, false, toKey, inclusive, false); + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if {@code fromKey} is null + * @throws IllegalArgumentException {@inheritDoc} + */ + public ConcurrentNavigableMap<K,V> tailMap(K fromKey, + boolean inclusive) { + if (fromKey == null) + throw new NullPointerException(); + return new SubMap<K,V> + (this, fromKey, inclusive, null, false, false); + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if {@code fromKey} or {@code toKey} is null + * @throws IllegalArgumentException {@inheritDoc} + */ + public ConcurrentNavigableMap<K,V> subMap(K fromKey, K toKey) { + return subMap(fromKey, true, toKey, false); + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if {@code toKey} is null + * @throws IllegalArgumentException {@inheritDoc} + */ + public ConcurrentNavigableMap<K,V> headMap(K toKey) { + return headMap(toKey, false); + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if {@code fromKey} is null + * @throws IllegalArgumentException {@inheritDoc} + */ + public ConcurrentNavigableMap<K,V> tailMap(K fromKey) { + return tailMap(fromKey, true); + } + + /* ---------------- Relational operations -------------- */ + + /** + * Returns a key-value mapping associated with the greatest key + * strictly less than the given key, or <tt>null</tt> if there is + * no such key. The returned entry does <em>not</em> support the + * <tt>Entry.setValue</tt> method. + * + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified key is null + */ + public Map.Entry<K,V> lowerEntry(K key) { + return getNear(key, LT); + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified key is null + */ + public K lowerKey(K key) { + Node<K,V> n = findNear(key, LT); + return (n == null)? null : n.key; + } + + /** + * Returns a key-value mapping associated with the greatest key + * less than or equal to the given key, or <tt>null</tt> if there + * is no such key. The returned entry does <em>not</em> support + * the <tt>Entry.setValue</tt> method. + * + * @param key the key + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified key is null + */ + public Map.Entry<K,V> floorEntry(K key) { + return getNear(key, LT|EQ); + } + + /** + * @param key the key + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified key is null + */ + public K floorKey(K key) { + Node<K,V> n = findNear(key, LT|EQ); + return (n == null)? null : n.key; + } + + /** + * Returns a key-value mapping associated with the least key + * greater than or equal to the given key, or <tt>null</tt> if + * there is no such entry. The returned entry does <em>not</em> + * support the <tt>Entry.setValue</tt> method. + * + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified key is null + */ + public Map.Entry<K,V> ceilingEntry(K key) { + return getNear(key, GT|EQ); + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified key is null + */ + public K ceilingKey(K key) { + Node<K,V> n = findNear(key, GT|EQ); + return (n == null)? null : n.key; + } + + /** + * Returns a key-value mapping associated with the least key + * strictly greater than the given key, or <tt>null</tt> if there + * is no such key. The returned entry does <em>not</em> support + * the <tt>Entry.setValue</tt> method. + * + * @param key the key + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified key is null + */ + public Map.Entry<K,V> higherEntry(K key) { + return getNear(key, GT); + } + + /** + * @param key the key + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified key is null + */ + public K higherKey(K key) { + Node<K,V> n = findNear(key, GT); + return (n == null)? null : n.key; + } + + /** + * Returns a key-value mapping associated with the least + * key in this map, or <tt>null</tt> if the map is empty. + * The returned entry does <em>not</em> support + * the <tt>Entry.setValue</tt> method. + */ + public Map.Entry<K,V> firstEntry() { + for (;;) { + Node<K,V> n = findFirst(); + if (n == null) + return null; + AbstractMap.SimpleImmutableEntry<K,V> e = n.createSnapshot(); + if (e != null) + return e; + } + } + + /** + * Returns a key-value mapping associated with the greatest + * key in this map, or <tt>null</tt> if the map is empty. + * The returned entry does <em>not</em> support + * the <tt>Entry.setValue</tt> method. + */ + public Map.Entry<K,V> lastEntry() { + for (;;) { + Node<K,V> n = findLast(); + if (n == null) + return null; + AbstractMap.SimpleImmutableEntry<K,V> e = n.createSnapshot(); + if (e != null) + return e; + } + } + + /** + * Removes and returns a key-value mapping associated with + * the least key in this map, or <tt>null</tt> if the map is empty. + * The returned entry does <em>not</em> support + * the <tt>Entry.setValue</tt> method. + */ + public Map.Entry<K,V> pollFirstEntry() { + return doRemoveFirstEntry(); + } + + /** + * Removes and returns a key-value mapping associated with + * the greatest key in this map, or <tt>null</tt> if the map is empty. + * The returned entry does <em>not</em> support + * the <tt>Entry.setValue</tt> method. + */ + public Map.Entry<K,V> pollLastEntry() { + return doRemoveLastEntry(); + } + + + /* ---------------- Iterators -------------- */ + + /** + * Base of iterator classes: + */ + abstract class Iter<T> implements Iterator<T> { + /** the last node returned by next() */ + Node<K,V> lastReturned; + /** the next node to return from next(); */ + Node<K,V> next; + /** Cache of next value field to maintain weak consistency */ + V nextValue; + + /** Initializes ascending iterator for entire range. */ + Iter() { + for (;;) { + next = findFirst(); + if (next == null) + break; + Object x = next.value; + if (x != null && x != next) { + nextValue = (V) x; + break; + } + } + } + + public final boolean hasNext() { + return next != null; + } + + /** Advances next to higher entry. */ + final void advance() { + if (next == null) + throw new NoSuchElementException(); + lastReturned = next; + for (;;) { + next = next.next; + if (next == null) + break; + Object x = next.value; + if (x != null && x != next) { + nextValue = (V) x; + break; + } + } + } + + public void remove() { + Node<K,V> l = lastReturned; + if (l == null) + throw new IllegalStateException(); + // It would not be worth all of the overhead to directly + // unlink from here. Using remove is fast enough. + ConcurrentSkipListMap.this.remove(l.key); + lastReturned = null; + } + + } + + final class ValueIterator extends Iter<V> { + public V next() { + V v = nextValue; + advance(); + return v; + } + } + + final class KeyIterator extends Iter<K> { + public K next() { + Node<K,V> n = next; + advance(); + return n.key; + } + } + + final class EntryIterator extends Iter<Map.Entry<K,V>> { + public Map.Entry<K,V> next() { + Node<K,V> n = next; + V v = nextValue; + advance(); + return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, v); + } + } + + // Factory methods for iterators needed by ConcurrentSkipListSet etc + + Iterator<K> keyIterator() { + return new KeyIterator(); + } + + Iterator<V> valueIterator() { + return new ValueIterator(); + } + + Iterator<Map.Entry<K,V>> entryIterator() { + return new EntryIterator(); + } + + /* ---------------- View Classes -------------- */ + + /* + * View classes are static, delegating to a ConcurrentNavigableMap + * to allow use by SubMaps, which outweighs the ugliness of + * needing type-tests for Iterator methods. + */ + + static final <E> List<E> toList(Collection<E> c) { + // Using size() here would be a pessimization. + List<E> list = new ArrayList<E>(); + for (E e : c) + list.add(e); + return list; + } + + static final class KeySet<E> extends AbstractSet<E> implements NavigableSet<E> { + private final ConcurrentNavigableMap<E,Object> m; + KeySet(ConcurrentNavigableMap<E,Object> map) { m = map; } + public int size() { return m.size(); } + public boolean isEmpty() { return m.isEmpty(); } + public boolean contains(Object o) { return m.containsKey(o); } + public boolean remove(Object o) { return m.remove(o) != null; } + public void clear() { m.clear(); } + public E lower(E e) { return m.lowerKey(e); } + public E floor(E e) { return m.floorKey(e); } + public E ceiling(E e) { return m.ceilingKey(e); } + public E higher(E e) { return m.higherKey(e); } + public Comparator<? super E> comparator() { return m.comparator(); } + public E first() { return m.firstKey(); } + public E last() { return m.lastKey(); } + public E pollFirst() { + Map.Entry<E,Object> e = m.pollFirstEntry(); + return e == null? null : e.getKey(); + } + public E pollLast() { + Map.Entry<E,Object> e = m.pollLastEntry(); + return e == null? null : e.getKey(); + } + public Iterator<E> iterator() { + if (m instanceof ConcurrentSkipListMap) + return ((ConcurrentSkipListMap<E,Object>)m).keyIterator(); + else + return ((ConcurrentSkipListMap.SubMap<E,Object>)m).keyIterator(); + } + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof Set)) + return false; + Collection<?> c = (Collection<?>) o; + try { + return containsAll(c) && c.containsAll(this); + } catch (ClassCastException unused) { + return false; + } catch (NullPointerException unused) { + return false; + } + } + public Object[] toArray() { return toList(this).toArray(); } + public <T> T[] toArray(T[] a) { return toList(this).toArray(a); } + public Iterator<E> descendingIterator() { + return descendingSet().iterator(); + } + public NavigableSet<E> subSet(E fromElement, + boolean fromInclusive, + E toElement, + boolean toInclusive) { + return new KeySet<E>(m.subMap(fromElement, fromInclusive, + toElement, toInclusive)); + } + public NavigableSet<E> headSet(E toElement, boolean inclusive) { + return new KeySet<E>(m.headMap(toElement, inclusive)); + } + public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { + return new KeySet<E>(m.tailMap(fromElement, inclusive)); + } + public NavigableSet<E> subSet(E fromElement, E toElement) { + return subSet(fromElement, true, toElement, false); + } + public NavigableSet<E> headSet(E toElement) { + return headSet(toElement, false); + } + public NavigableSet<E> tailSet(E fromElement) { + return tailSet(fromElement, true); + } + public NavigableSet<E> descendingSet() { + return new KeySet(m.descendingMap()); + } + } + + static final class Values<E> extends AbstractCollection<E> { + private final ConcurrentNavigableMap<Object, E> m; + Values(ConcurrentNavigableMap<Object, E> map) { + m = map; + } + public Iterator<E> iterator() { + if (m instanceof ConcurrentSkipListMap) + return ((ConcurrentSkipListMap<Object,E>)m).valueIterator(); + else + return ((SubMap<Object,E>)m).valueIterator(); + } + public boolean isEmpty() { + return m.isEmpty(); + } + public int size() { + return m.size(); + } + public boolean contains(Object o) { + return m.containsValue(o); + } + public void clear() { + m.clear(); + } + public Object[] toArray() { return toList(this).toArray(); } + public <T> T[] toArray(T[] a) { return toList(this).toArray(a); } + } + + static final class EntrySet<K1,V1> extends AbstractSet<Map.Entry<K1,V1>> { + private final ConcurrentNavigableMap<K1, V1> m; + EntrySet(ConcurrentNavigableMap<K1, V1> map) { + m = map; + } + + public Iterator<Map.Entry<K1,V1>> iterator() { + if (m instanceof ConcurrentSkipListMap) + return ((ConcurrentSkipListMap<K1,V1>)m).entryIterator(); + else + return ((SubMap<K1,V1>)m).entryIterator(); + } + + public boolean contains(Object o) { + if (!(o instanceof Map.Entry)) + return false; + Map.Entry<K1,V1> e = (Map.Entry<K1,V1>)o; + V1 v = m.get(e.getKey()); + return v != null && v.equals(e.getValue()); + } + public boolean remove(Object o) { + if (!(o instanceof Map.Entry)) + return false; + Map.Entry<K1,V1> e = (Map.Entry<K1,V1>)o; + return m.remove(e.getKey(), + e.getValue()); + } + public boolean isEmpty() { + return m.isEmpty(); + } + public int size() { + return m.size(); + } + public void clear() { + m.clear(); + } + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof Set)) + return false; + Collection<?> c = (Collection<?>) o; + try { + return containsAll(c) && c.containsAll(this); + } catch (ClassCastException unused) { + return false; + } catch (NullPointerException unused) { + return false; + } + } + public Object[] toArray() { return toList(this).toArray(); } + public <T> T[] toArray(T[] a) { return toList(this).toArray(a); } + } + + /** + * Submaps returned by {@link ConcurrentSkipListMap} submap operations + * represent a subrange of mappings of their underlying + * maps. Instances of this class support all methods of their + * underlying maps, differing in that mappings outside their range are + * ignored, and attempts to add mappings outside their ranges result + * in {@link IllegalArgumentException}. Instances of this class are + * constructed only using the <tt>subMap</tt>, <tt>headMap</tt>, and + * <tt>tailMap</tt> methods of their underlying maps. + * + * @serial include + */ + static final class SubMap<K,V> extends AbstractMap<K,V> + implements ConcurrentNavigableMap<K,V>, Cloneable, + java.io.Serializable { + private static final long serialVersionUID = -7647078645895051609L; + + /** Underlying map */ + private final ConcurrentSkipListMap<K,V> m; + /** lower bound key, or null if from start */ + private final K lo; + /** upper bound key, or null if to end */ + private final K hi; + /** inclusion flag for lo */ + private final boolean loInclusive; + /** inclusion flag for hi */ + private final boolean hiInclusive; + /** direction */ + private final boolean isDescending; + + // Lazily initialized view holders + private transient KeySet<K> keySetView; + private transient Set<Map.Entry<K,V>> entrySetView; + private transient Collection<V> valuesView; + + /** + * Creates a new submap, initializing all fields + */ + SubMap(ConcurrentSkipListMap<K,V> map, + K fromKey, boolean fromInclusive, + K toKey, boolean toInclusive, + boolean isDescending) { + if (fromKey != null && toKey != null && + map.compare(fromKey, toKey) > 0) + throw new IllegalArgumentException("inconsistent range"); + this.m = map; + this.lo = fromKey; + this.hi = toKey; + this.loInclusive = fromInclusive; + this.hiInclusive = toInclusive; + this.isDescending = isDescending; + } + + /* ---------------- Utilities -------------- */ + + private boolean tooLow(K key) { + if (lo != null) { + int c = m.compare(key, lo); + if (c < 0 || (c == 0 && !loInclusive)) + return true; + } + return false; + } + + private boolean tooHigh(K key) { + if (hi != null) { + int c = m.compare(key, hi); + if (c > 0 || (c == 0 && !hiInclusive)) + return true; + } + return false; + } + + private boolean inBounds(K key) { + return !tooLow(key) && !tooHigh(key); + } + + private void checkKeyBounds(K key) throws IllegalArgumentException { + if (key == null) + throw new NullPointerException(); + if (!inBounds(key)) + throw new IllegalArgumentException("key out of range"); + } + + /** + * Returns true if node key is less than upper bound of range + */ + private boolean isBeforeEnd(ConcurrentSkipListMap.Node<K,V> n) { + if (n == null) + return false; + if (hi == null) + return true; + K k = n.key; + if (k == null) // pass by markers and headers + return true; + int c = m.compare(k, hi); + if (c > 0 || (c == 0 && !hiInclusive)) + return false; + return true; + } + + /** + * Returns lowest node. This node might not be in range, so + * most usages need to check bounds + */ + private ConcurrentSkipListMap.Node<K,V> loNode() { + if (lo == null) + return m.findFirst(); + else if (loInclusive) + return m.findNear(lo, m.GT|m.EQ); + else + return m.findNear(lo, m.GT); + } + + /** + * Returns highest node. This node might not be in range, so + * most usages need to check bounds + */ + private ConcurrentSkipListMap.Node<K,V> hiNode() { + if (hi == null) + return m.findLast(); + else if (hiInclusive) + return m.findNear(hi, m.LT|m.EQ); + else + return m.findNear(hi, m.LT); + } + + /** + * Returns lowest absolute key (ignoring directonality) + */ + private K lowestKey() { + ConcurrentSkipListMap.Node<K,V> n = loNode(); + if (isBeforeEnd(n)) + return n.key; + else + throw new NoSuchElementException(); + } + + /** + * Returns highest absolute key (ignoring directonality) + */ + private K highestKey() { + ConcurrentSkipListMap.Node<K,V> n = hiNode(); + if (n != null) { + K last = n.key; + if (inBounds(last)) + return last; + } + throw new NoSuchElementException(); + } + + private Map.Entry<K,V> lowestEntry() { + for (;;) { + ConcurrentSkipListMap.Node<K,V> n = loNode(); + if (!isBeforeEnd(n)) + return null; + Map.Entry<K,V> e = n.createSnapshot(); + if (e != null) + return e; + } + } + + private Map.Entry<K,V> highestEntry() { + for (;;) { + ConcurrentSkipListMap.Node<K,V> n = hiNode(); + if (n == null || !inBounds(n.key)) + return null; + Map.Entry<K,V> e = n.createSnapshot(); + if (e != null) + return e; + } + } + + private Map.Entry<K,V> removeLowest() { + for (;;) { + Node<K,V> n = loNode(); + if (n == null) + return null; + K k = n.key; + if (!inBounds(k)) + return null; + V v = m.doRemove(k, null); + if (v != null) + return new AbstractMap.SimpleImmutableEntry<K,V>(k, v); + } + } + + private Map.Entry<K,V> removeHighest() { + for (;;) { + Node<K,V> n = hiNode(); + if (n == null) + return null; + K k = n.key; + if (!inBounds(k)) + return null; + V v = m.doRemove(k, null); + if (v != null) + return new AbstractMap.SimpleImmutableEntry<K,V>(k, v); + } + } + + /** + * Submap version of ConcurrentSkipListMap.getNearEntry + */ + private Map.Entry<K,V> getNearEntry(K key, int rel) { + if (isDescending) { // adjust relation for direction + if ((rel & m.LT) == 0) + rel |= m.LT; + else + rel &= ~m.LT; + } + if (tooLow(key)) + return ((rel & m.LT) != 0)? null : lowestEntry(); + if (tooHigh(key)) + return ((rel & m.LT) != 0)? highestEntry() : null; + for (;;) { + Node<K,V> n = m.findNear(key, rel); + if (n == null || !inBounds(n.key)) + return null; + K k = n.key; + V v = n.getValidValue(); + if (v != null) + return new AbstractMap.SimpleImmutableEntry<K,V>(k, v); + } + } + + // Almost the same as getNearEntry, except for keys + private K getNearKey(K key, int rel) { + if (isDescending) { // adjust relation for direction + if ((rel & m.LT) == 0) + rel |= m.LT; + else + rel &= ~m.LT; + } + if (tooLow(key)) { + if ((rel & m.LT) == 0) { + ConcurrentSkipListMap.Node<K,V> n = loNode(); + if (isBeforeEnd(n)) + return n.key; + } + return null; + } + if (tooHigh(key)) { + if ((rel & m.LT) != 0) { + ConcurrentSkipListMap.Node<K,V> n = hiNode(); + if (n != null) { + K last = n.key; + if (inBounds(last)) + return last; + } + } + return null; + } + for (;;) { + Node<K,V> n = m.findNear(key, rel); + if (n == null || !inBounds(n.key)) + return null; + K k = n.key; + V v = n.getValidValue(); + if (v != null) + return k; + } + } + + /* ---------------- Map API methods -------------- */ + + public boolean containsKey(Object key) { + if (key == null) throw new NullPointerException(); + K k = (K)key; + return inBounds(k) && m.containsKey(k); + } + + public V get(Object key) { + if (key == null) throw new NullPointerException(); + K k = (K)key; + return ((!inBounds(k)) ? null : m.get(k)); + } + + public V put(K key, V value) { + checkKeyBounds(key); + return m.put(key, value); + } + + public V remove(Object key) { + K k = (K)key; + return (!inBounds(k))? null : m.remove(k); + } + + public int size() { + long count = 0; + for (ConcurrentSkipListMap.Node<K,V> n = loNode(); + isBeforeEnd(n); + n = n.next) { + if (n.getValidValue() != null) + ++count; + } + return count >= Integer.MAX_VALUE? Integer.MAX_VALUE : (int)count; + } + + public boolean isEmpty() { + return !isBeforeEnd(loNode()); + } + + public boolean containsValue(Object value) { + if (value == null) + throw new NullPointerException(); + for (ConcurrentSkipListMap.Node<K,V> n = loNode(); + isBeforeEnd(n); + n = n.next) { + V v = n.getValidValue(); + if (v != null && value.equals(v)) + return true; + } + return false; + } + + public void clear() { + for (ConcurrentSkipListMap.Node<K,V> n = loNode(); + isBeforeEnd(n); + n = n.next) { + if (n.getValidValue() != null) + m.remove(n.key); + } + } + + /* ---------------- ConcurrentMap API methods -------------- */ + + public V putIfAbsent(K key, V value) { + checkKeyBounds(key); + return m.putIfAbsent(key, value); + } + + public boolean remove(Object key, Object value) { + K k = (K)key; + return inBounds(k) && m.remove(k, value); + } + + public boolean replace(K key, V oldValue, V newValue) { + checkKeyBounds(key); + return m.replace(key, oldValue, newValue); + } + + public V replace(K key, V value) { + checkKeyBounds(key); + return m.replace(key, value); + } + + /* ---------------- SortedMap API methods -------------- */ + + public Comparator<? super K> comparator() { + Comparator<? super K> cmp = m.comparator(); + if (isDescending) + return Collections.reverseOrder(cmp); + else + return cmp; + } + + /** + * Utility to create submaps, where given bounds override + * unbounded(null) ones and/or are checked against bounded ones. + */ + private SubMap<K,V> newSubMap(K fromKey, + boolean fromInclusive, + K toKey, + boolean toInclusive) { + if (isDescending) { // flip senses + K tk = fromKey; + fromKey = toKey; + toKey = tk; + boolean ti = fromInclusive; + fromInclusive = toInclusive; + toInclusive = ti; + } + if (lo != null) { + if (fromKey == null) { + fromKey = lo; + fromInclusive = loInclusive; + } + else { + int c = m.compare(fromKey, lo); + if (c < 0 || (c == 0 && !loInclusive && fromInclusive)) + throw new IllegalArgumentException("key out of range"); + } + } + if (hi != null) { + if (toKey == null) { + toKey = hi; + toInclusive = hiInclusive; + } + else { + int c = m.compare(toKey, hi); + if (c > 0 || (c == 0 && !hiInclusive && toInclusive)) + throw new IllegalArgumentException("key out of range"); + } + } + return new SubMap<K,V>(m, fromKey, fromInclusive, + toKey, toInclusive, isDescending); + } + + public SubMap<K,V> subMap(K fromKey, + boolean fromInclusive, + K toKey, + boolean toInclusive) { + if (fromKey == null || toKey == null) + throw new NullPointerException(); + return newSubMap(fromKey, fromInclusive, toKey, toInclusive); + } + + public SubMap<K,V> headMap(K toKey, + boolean inclusive) { + if (toKey == null) + throw new NullPointerException(); + return newSubMap(null, false, toKey, inclusive); + } + + public SubMap<K,V> tailMap(K fromKey, + boolean inclusive) { + if (fromKey == null) + throw new NullPointerException(); + return newSubMap(fromKey, inclusive, null, false); + } + + public SubMap<K,V> subMap(K fromKey, K toKey) { + return subMap(fromKey, true, toKey, false); + } + + public SubMap<K,V> headMap(K toKey) { + return headMap(toKey, false); + } + + public SubMap<K,V> tailMap(K fromKey) { + return tailMap(fromKey, true); + } + + public SubMap<K,V> descendingMap() { + return new SubMap<K,V>(m, lo, loInclusive, + hi, hiInclusive, !isDescending); + } + + /* ---------------- Relational methods -------------- */ + + public Map.Entry<K,V> ceilingEntry(K key) { + return getNearEntry(key, (m.GT|m.EQ)); + } + + public K ceilingKey(K key) { + return getNearKey(key, (m.GT|m.EQ)); + } + + public Map.Entry<K,V> lowerEntry(K key) { + return getNearEntry(key, (m.LT)); + } + + public K lowerKey(K key) { + return getNearKey(key, (m.LT)); + } + + public Map.Entry<K,V> floorEntry(K key) { + return getNearEntry(key, (m.LT|m.EQ)); + } + + public K floorKey(K key) { + return getNearKey(key, (m.LT|m.EQ)); + } + + public Map.Entry<K,V> higherEntry(K key) { + return getNearEntry(key, (m.GT)); + } + + public K higherKey(K key) { + return getNearKey(key, (m.GT)); + } + + public K firstKey() { + return isDescending? highestKey() : lowestKey(); + } + + public K lastKey() { + return isDescending? lowestKey() : highestKey(); + } + + public Map.Entry<K,V> firstEntry() { + return isDescending? highestEntry() : lowestEntry(); + } + + public Map.Entry<K,V> lastEntry() { + return isDescending? lowestEntry() : highestEntry(); + } + + public Map.Entry<K,V> pollFirstEntry() { + return isDescending? removeHighest() : removeLowest(); + } + + public Map.Entry<K,V> pollLastEntry() { + return isDescending? removeLowest() : removeHighest(); + } + + /* ---------------- Submap Views -------------- */ + + public NavigableSet<K> keySet() { + KeySet<K> ks = keySetView; + return (ks != null) ? ks : (keySetView = new KeySet(this)); + } + + public NavigableSet<K> navigableKeySet() { + KeySet<K> ks = keySetView; + return (ks != null) ? ks : (keySetView = new KeySet(this)); + } + + public Collection<V> values() { + Collection<V> vs = valuesView; + return (vs != null) ? vs : (valuesView = new Values(this)); + } + + public Set<Map.Entry<K,V>> entrySet() { + Set<Map.Entry<K,V>> es = entrySetView; + return (es != null) ? es : (entrySetView = new EntrySet(this)); + } + + public NavigableSet<K> descendingKeySet() { + return descendingMap().navigableKeySet(); + } + + Iterator<K> keyIterator() { + return new SubMapKeyIterator(); + } + + Iterator<V> valueIterator() { + return new SubMapValueIterator(); + } + + Iterator<Map.Entry<K,V>> entryIterator() { + return new SubMapEntryIterator(); + } + + /** + * Variant of main Iter class to traverse through submaps. + */ + abstract class SubMapIter<T> implements Iterator<T> { + /** the last node returned by next() */ + Node<K,V> lastReturned; + /** the next node to return from next(); */ + Node<K,V> next; + /** Cache of next value field to maintain weak consistency */ + V nextValue; + + SubMapIter() { + for (;;) { + next = isDescending ? hiNode() : loNode(); + if (next == null) + break; + Object x = next.value; + if (x != null && x != next) { + if (! inBounds(next.key)) + next = null; + else + nextValue = (V) x; + break; + } + } + } + + public final boolean hasNext() { + return next != null; + } + + final void advance() { + if (next == null) + throw new NoSuchElementException(); + lastReturned = next; + if (isDescending) + descend(); + else + ascend(); + } + + private void ascend() { + for (;;) { + next = next.next; + if (next == null) + break; + Object x = next.value; + if (x != null && x != next) { + if (tooHigh(next.key)) + next = null; + else + nextValue = (V) x; + break; + } + } + } + + private void descend() { + for (;;) { + next = m.findNear(lastReturned.key, LT); + if (next == null) + break; + Object x = next.value; + if (x != null && x != next) { + if (tooLow(next.key)) + next = null; + else + nextValue = (V) x; + break; + } + } + } + + public void remove() { + Node<K,V> l = lastReturned; + if (l == null) + throw new IllegalStateException(); + m.remove(l.key); + lastReturned = null; + } + + } + + final class SubMapValueIterator extends SubMapIter<V> { + public V next() { + V v = nextValue; + advance(); + return v; + } + } + + final class SubMapKeyIterator extends SubMapIter<K> { + public K next() { + Node<K,V> n = next; + advance(); + return n.key; + } + } + + final class SubMapEntryIterator extends SubMapIter<Map.Entry<K,V>> { + public Map.Entry<K,V> next() { + Node<K,V> n = next; + V v = nextValue; + advance(); + return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, v); + } + } + } +} diff --git a/concurrent/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java b/concurrent/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java new file mode 100644 index 0000000..7fd1c76 --- /dev/null +++ b/concurrent/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java @@ -0,0 +1,456 @@ +/* + * 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; +import java.util.*; +import sun.misc.Unsafe; + +/** + * A scalable concurrent {@link NavigableSet} implementation based on + * a {@link ConcurrentSkipListMap}. The elements of the set are kept + * sorted according to their {@linkplain Comparable natural ordering}, + * or by a {@link Comparator} provided at set creation time, depending + * on which constructor is used. + * + * <p>This implementation provides expected average <i>log(n)</i> time + * cost for the <tt>contains</tt>, <tt>add</tt>, and <tt>remove</tt> + * operations and their variants. Insertion, removal, and access + * operations safely execute concurrently by multiple threads. + * Iterators are <i>weakly consistent</i>, returning elements + * reflecting the state of the set at some point at or since the + * creation of the iterator. They do <em>not</em> throw {@link + * ConcurrentModificationException}, and may proceed concurrently with + * other operations. Ascending ordered views and their iterators are + * faster than descending ones. + * + * <p>Beware that, unlike in most collections, the <tt>size</tt> + * method is <em>not</em> a constant-time operation. Because of the + * asynchronous nature of these sets, determining the current number + * of elements requires a traversal of the elements. Additionally, the + * bulk operations <tt>addAll</tt>, <tt>removeAll</tt>, + * <tt>retainAll</tt>, and <tt>containsAll</tt> are <em>not</em> + * guaranteed to be performed atomically. For example, an iterator + * operating concurrently with an <tt>addAll</tt> operation might view + * only some of the added elements. + * + * <p>This class and its iterators implement all of the + * <em>optional</em> methods of the {@link Set} and {@link Iterator} + * interfaces. Like most other concurrent collection implementations, + * this class does not permit the use of <tt>null</tt> elements, + * because <tt>null</tt> arguments and return values cannot be reliably + * distinguished from the absence of elements. + * + * <p>This class is a member of the + * <a href="{@docRoot}/../technotes/guides/collections/index.html"> + * Java Collections Framework</a>. + * + * @author Doug Lea + * @param <E> the type of elements maintained by this set + * @since 1.6 + */ +public class ConcurrentSkipListSet<E> + extends AbstractSet<E> + implements NavigableSet<E>, Cloneable, java.io.Serializable { + + private static final long serialVersionUID = -2479143111061671589L; + + /** + * The underlying map. Uses Boolean.TRUE as value for each + * element. This field is declared final for the sake of thread + * safety, which entails some ugliness in clone() + */ + private final ConcurrentNavigableMap<E,Object> m; + + /** + * Constructs a new, empty set that orders its elements according to + * their {@linkplain Comparable natural ordering}. + */ + public ConcurrentSkipListSet() { + m = new ConcurrentSkipListMap<E,Object>(); + } + + /** + * Constructs a new, empty set that orders its elements according to + * the specified comparator. + * + * @param comparator the comparator that will be used to order this set. + * If <tt>null</tt>, the {@linkplain Comparable natural + * ordering} of the elements will be used. + */ + public ConcurrentSkipListSet(Comparator<? super E> comparator) { + m = new ConcurrentSkipListMap<E,Object>(comparator); + } + + /** + * Constructs a new set containing the elements in the specified + * collection, that orders its elements according to their + * {@linkplain Comparable natural ordering}. + * + * @param c The elements that will comprise the new set + * @throws ClassCastException if the elements in <tt>c</tt> are + * not {@link Comparable}, or are not mutually comparable + * @throws NullPointerException if the specified collection or any + * of its elements are null + */ + public ConcurrentSkipListSet(Collection<? extends E> c) { + m = new ConcurrentSkipListMap<E,Object>(); + addAll(c); + } + + /** + * Constructs a new set containing the same elements and using the + * same ordering as the specified sorted set. + * + * @param s sorted set whose elements will comprise the new set + * @throws NullPointerException if the specified sorted set or any + * of its elements are null + */ + public ConcurrentSkipListSet(SortedSet<E> s) { + m = new ConcurrentSkipListMap<E,Object>(s.comparator()); + addAll(s); + } + + /** + * For use by submaps + */ + ConcurrentSkipListSet(ConcurrentNavigableMap<E,Object> m) { + this.m = m; + } + + /** + * Returns a shallow copy of this <tt>ConcurrentSkipListSet</tt> + * instance. (The elements themselves are not cloned.) + * + * @return a shallow copy of this set + */ + public ConcurrentSkipListSet<E> clone() { + ConcurrentSkipListSet<E> clone = null; + try { + clone = (ConcurrentSkipListSet<E>) super.clone(); + clone.setMap(new ConcurrentSkipListMap(m)); + } catch (CloneNotSupportedException e) { + throw new InternalError(); + } + + return clone; + } + + /* ---------------- Set operations -------------- */ + + /** + * Returns the number of elements in this set. If this set + * contains more than <tt>Integer.MAX_VALUE</tt> elements, it + * returns <tt>Integer.MAX_VALUE</tt>. + * + * <p>Beware that, unlike in most collections, this method is + * <em>NOT</em> a constant-time operation. Because of the + * asynchronous nature of these sets, determining the current + * number of elements requires traversing them all to count them. + * Additionally, it is possible for the size to change during + * execution of this method, in which case the returned result + * will be inaccurate. Thus, this method is typically not very + * useful in concurrent applications. + * + * @return the number of elements in this set + */ + public int size() { + return m.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 m.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.equals(e)</tt>. + * + * @param o object to be checked for containment in this set + * @return <tt>true</tt> if this set contains the specified element + * @throws ClassCastException if the specified element cannot be + * compared with the elements currently in this set + * @throws NullPointerException if the specified element is null + */ + public boolean contains(Object o) { + return m.containsKey(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.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 + * @throws ClassCastException if <tt>e</tt> cannot be compared + * with the elements currently in this set + * @throws NullPointerException if the specified element is null + */ + public boolean add(E e) { + return m.putIfAbsent(e, Boolean.TRUE) == null; + } + + /** + * Removes the specified element from this set if it is present. + * More formally, removes an element <tt>e</tt> such that + * <tt>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 + * @throws ClassCastException if <tt>o</tt> cannot be compared + * with the elements currently in this set + * @throws NullPointerException if the specified element is null + */ + public boolean remove(Object o) { + return m.remove(o, Boolean.TRUE); + } + + /** + * Removes all of the elements from this set. + */ + public void clear() { + m.clear(); + } + + /** + * Returns an iterator over the elements in this set in ascending order. + * + * @return an iterator over the elements in this set in ascending order + */ + public Iterator<E> iterator() { + return m.navigableKeySet().iterator(); + } + + /** + * Returns an iterator over the elements in this set in descending order. + * + * @return an iterator over the elements in this set in descending order + */ + public Iterator<E> descendingIterator() { + return m.descendingKeySet().iterator(); + } + + + /* ---------------- AbstractSet Overrides -------------- */ + + /** + * Compares the specified object with this set for equality. Returns + * <tt>true</tt> if the specified object is also a set, the two sets + * have the same size, and every member of the specified set is + * contained in this set (or equivalently, every member of this set is + * contained in the specified set). This definition ensures that the + * equals method works properly across different implementations of the + * set interface. + * + * @param o the object to be compared for equality with this set + * @return <tt>true</tt> if the specified object is equal to this set + */ + public boolean equals(Object o) { + // Override AbstractSet version to avoid calling size() + if (o == this) + return true; + if (!(o instanceof Set)) + return false; + Collection<?> c = (Collection<?>) o; + try { + return containsAll(c) && c.containsAll(this); + } catch (ClassCastException unused) { + return false; + } catch (NullPointerException unused) { + return false; + } + } + + /** + * 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 types of one or more elements in this + * set are incompatible with the specified collection + * @throws NullPointerException if the specified collection or any + * of its elements are null + */ + public boolean removeAll(Collection<?> c) { + // Override AbstractSet version to avoid unnecessary call to size() + boolean modified = false; + for (Iterator<?> i = c.iterator(); i.hasNext(); ) + if (remove(i.next())) + modified = true; + return modified; + } + + /* ---------------- Relational operations -------------- */ + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified element is null + */ + public E lower(E e) { + return m.lowerKey(e); + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified element is null + */ + public E floor(E e) { + return m.floorKey(e); + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified element is null + */ + public E ceiling(E e) { + return m.ceilingKey(e); + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if the specified element is null + */ + public E higher(E e) { + return m.higherKey(e); + } + + public E pollFirst() { + Map.Entry<E,Object> e = m.pollFirstEntry(); + return e == null? null : e.getKey(); + } + + public E pollLast() { + Map.Entry<E,Object> e = m.pollLastEntry(); + return e == null? null : e.getKey(); + } + + + /* ---------------- SortedSet operations -------------- */ + + + public Comparator<? super E> comparator() { + return m.comparator(); + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E first() { + return m.firstKey(); + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E last() { + return m.lastKey(); + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if {@code fromElement} or + * {@code toElement} is null + * @throws IllegalArgumentException {@inheritDoc} + */ + public NavigableSet<E> subSet(E fromElement, + boolean fromInclusive, + E toElement, + boolean toInclusive) { + return new ConcurrentSkipListSet<E> + (m.subMap(fromElement, fromInclusive, + toElement, toInclusive)); + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if {@code toElement} is null + * @throws IllegalArgumentException {@inheritDoc} + */ + public NavigableSet<E> headSet(E toElement, boolean inclusive) { + return new ConcurrentSkipListSet<E>(m.headMap(toElement, inclusive)); + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if {@code fromElement} is null + * @throws IllegalArgumentException {@inheritDoc} + */ + public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { + return new ConcurrentSkipListSet<E>(m.tailMap(fromElement, inclusive)); + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if {@code fromElement} or + * {@code toElement} is null + * @throws IllegalArgumentException {@inheritDoc} + */ + public NavigableSet<E> subSet(E fromElement, E toElement) { + return subSet(fromElement, true, toElement, false); + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if {@code toElement} is null + * @throws IllegalArgumentException {@inheritDoc} + */ + public NavigableSet<E> headSet(E toElement) { + return headSet(toElement, false); + } + + /** + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException if {@code fromElement} is null + * @throws IllegalArgumentException {@inheritDoc} + */ + public NavigableSet<E> tailSet(E fromElement) { + return tailSet(fromElement, true); + } + + /** + * Returns a reverse order view of the elements contained in this set. + * The descending set is backed by this set, so changes to the set are + * reflected in the descending set, and vice-versa. + * + * <p>The returned set has an ordering equivalent to + * <tt>{@link Collections#reverseOrder(Comparator) Collections.reverseOrder}(comparator())</tt>. + * The expression {@code s.descendingSet().descendingSet()} returns a + * view of {@code s} essentially equivalent to {@code s}. + * + * @return a reverse order view of this set + */ + public NavigableSet<E> descendingSet() { + return new ConcurrentSkipListSet(m.descendingMap()); + } + + // Support for resetting map in clone + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final long mapOffset; + static { + try { + mapOffset = unsafe.objectFieldOffset + (ConcurrentSkipListSet.class.getDeclaredField("m")); + } catch (Exception ex) { throw new Error(ex); } + } + private void setMap(ConcurrentNavigableMap<E,Object> map) { + unsafe.putObjectVolatile(this, mapOffset, map); + } + +} diff --git a/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java b/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java index f0c8ac6..237c5df 100644 --- a/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java +++ b/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java @@ -17,8 +17,6 @@ package java.util.concurrent; import java.util.*; import java.util.concurrent.locks.*; -import java.lang.reflect.Array; - import sun.misc.Unsafe; // BEGIN android-note @@ -102,7 +100,7 @@ public class CopyOnWriteArrayList<E> 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); + elements = Arrays.copyOf(elements, elements.length, Object[].class); setArray(elements); } @@ -114,7 +112,7 @@ public class CopyOnWriteArrayList<E> * @throws NullPointerException if the specified array is null */ public CopyOnWriteArrayList(E[] toCopyIn) { - setArray(Java6Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class)); + setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class)); } /** @@ -288,7 +286,7 @@ public class CopyOnWriteArrayList<E> */ public Object[] toArray() { Object[] elements = getArray(); - return Java6Arrays.copyOf(elements, elements.length); + return Arrays.copyOf(elements, elements.length); } /** @@ -335,7 +333,7 @@ public class CopyOnWriteArrayList<E> Object[] elements = getArray(); int len = elements.length; if (a.length < len) - return (T[]) Java6Arrays.copyOf(elements, len, a.getClass()); + return (T[]) Arrays.copyOf(elements, len, a.getClass()); else { System.arraycopy(elements, 0, a, 0, len); if (a.length > len) @@ -375,7 +373,7 @@ public class CopyOnWriteArrayList<E> if (oldValue != element) { int len = elements.length; - Object[] newElements = Java6Arrays.copyOf(elements, len); + Object[] newElements = Arrays.copyOf(elements, len); newElements[index] = element; setArray(newElements); } else { @@ -400,7 +398,7 @@ public class CopyOnWriteArrayList<E> try { Object[] elements = getArray(); int len = elements.length; - Object[] newElements = Java6Arrays.copyOf(elements, len + 1); + Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; @@ -428,7 +426,7 @@ public class CopyOnWriteArrayList<E> Object[] newElements; int numMoved = len - index; if (numMoved == 0) - newElements = Java6Arrays.copyOf(elements, len + 1); + newElements = Arrays.copyOf(elements, len + 1); else { newElements = new Object[len + 1]; System.arraycopy(elements, 0, newElements, 0, index); @@ -458,7 +456,7 @@ public class CopyOnWriteArrayList<E> E oldValue = get(elements, index); int numMoved = len - index - 1; if (numMoved == 0) - setArray(Java6Arrays.copyOf(elements, len - 1)); + setArray(Arrays.copyOf(elements, len - 1)); else { Object[] newElements = new Object[len - 1]; System.arraycopy(elements, 0, newElements, 0, index); @@ -544,7 +542,7 @@ public class CopyOnWriteArrayList<E> int newlen = len - (toIndex - fromIndex); int numMoved = len - toIndex; if (numMoved == 0) - setArray(Java6Arrays.copyOf(elements, newlen)); + setArray(Arrays.copyOf(elements, newlen)); else { Object[] newElements = new Object[newlen]; System.arraycopy(elements, 0, newElements, 0, fromIndex); @@ -636,7 +634,7 @@ public class CopyOnWriteArrayList<E> temp[newlen++] = element; } if (newlen != len) { - setArray(Java6Arrays.copyOf(temp, newlen)); + setArray(Arrays.copyOf(temp, newlen)); return true; } } @@ -676,7 +674,7 @@ public class CopyOnWriteArrayList<E> temp[newlen++] = element; } if (newlen != len) { - setArray(Java6Arrays.copyOf(temp, newlen)); + setArray(Arrays.copyOf(temp, newlen)); return true; } } @@ -715,7 +713,7 @@ public class CopyOnWriteArrayList<E> uniq[added++] = e; } if (added > 0) { - Object[] newElements = Java6Arrays.copyOf(elements, len + added); + Object[] newElements = Arrays.copyOf(elements, len + added); System.arraycopy(uniq, 0, newElements, len, added); setArray(newElements); } @@ -758,7 +756,7 @@ public class CopyOnWriteArrayList<E> try { Object[] elements = getArray(); int len = elements.length; - Object[] newElements = Java6Arrays.copyOf(elements, len + cs.length); + Object[] newElements = Arrays.copyOf(elements, len + cs.length); System.arraycopy(cs, 0, newElements, len, cs.length); setArray(newElements); return true; @@ -798,7 +796,7 @@ public class CopyOnWriteArrayList<E> int numMoved = len - index; Object[] newElements; if (numMoved == 0) - newElements = Java6Arrays.copyOf(elements, len + cs.length); + newElements = Arrays.copyOf(elements, len + cs.length); else { newElements = new Object[len + cs.length]; System.arraycopy(elements, 0, newElements, 0, index); @@ -1314,4 +1312,5 @@ public class CopyOnWriteArrayList<E> private void resetLock() { unsafe.putObjectVolatile(this, lockOffset, new ReentrantLock()); } + } diff --git a/concurrent/src/main/java/java/util/concurrent/Delayed.java b/concurrent/src/main/java/java/util/concurrent/Delayed.java index af41300..b1ff4ee 100644 --- a/concurrent/src/main/java/java/util/concurrent/Delayed.java +++ b/concurrent/src/main/java/java/util/concurrent/Delayed.java @@ -4,19 +4,10 @@ * http://creativecommons.org/licenses/publicdomain */ -/* - * Modified in Apache Harmony to comply with Java 5 signature - * specification. - */ - package java.util.concurrent; import java.util.*; -// BEGIN android-note -// Added generic type Delayed to Comparable to be closer to the RI. -// END android-note - /** * A mix-in style interface for marking objects that should be * acted upon after a given delay. diff --git a/concurrent/src/main/java/java/util/concurrent/Exchanger.java b/concurrent/src/main/java/java/util/concurrent/Exchanger.java index f67659c..bb2193c 100644 --- a/concurrent/src/main/java/java/util/concurrent/Exchanger.java +++ b/concurrent/src/main/java/java/util/concurrent/Exchanger.java @@ -471,7 +471,7 @@ public class Exchanger<V> { else if (w.isInterrupted()) // Abort on interrupt tryCancel(node, slot); else // Block - LockSupport.park(); + LockSupport.park(node); } } @@ -506,7 +506,7 @@ public class Exchanger<V> { else if (w.isInterrupted()) tryCancel(node, slot); else - LockSupport.parkNanos(nanos); + LockSupport.parkNanos(node, nanos); } else if (tryCancel(node, slot) && !w.isInterrupted()) return scanOnTimeout(node); diff --git a/concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java b/concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java index 02606f9..bf4a584 100644 --- a/concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java +++ b/concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java @@ -84,7 +84,7 @@ public class ExecutorCompletionService<V> implements CompletionService<V> { * FutureTask extension to enqueue upon completion */ private class QueueingFuture extends FutureTask<Void> { - QueueingFuture(FutureTask<V> task) { + QueueingFuture(RunnableFuture<V> task) { super(task, null); this.task = task; } @@ -92,12 +92,18 @@ public class ExecutorCompletionService<V> implements CompletionService<V> { private final Future<V> task; } - private FutureTask<V> newTaskFor(Callable<V> task) { - return new FutureTask<V>(task); + private RunnableFuture<V> newTaskFor(Callable<V> task) { + if (aes == null) + return new FutureTask<V>(task); + else + return aes.newTaskFor(task); } - private FutureTask<V> newTaskFor(Runnable task, V result) { - return new FutureTask<V>(task, result); + private RunnableFuture<V> newTaskFor(Runnable task, V result) { + if (aes == null) + return new FutureTask<V>(task, result); + else + return aes.newTaskFor(task, result); } /** @@ -142,14 +148,14 @@ public class ExecutorCompletionService<V> implements CompletionService<V> { public Future<V> submit(Callable<V> task) { if (task == null) throw new NullPointerException(); - FutureTask<V> f = newTaskFor(task); + RunnableFuture<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(); - FutureTask<V> f = newTaskFor(task, result); + RunnableFuture<V> f = newTaskFor(task, result); executor.execute(new QueueingFuture(f)); return f; } diff --git a/concurrent/src/main/java/java/util/concurrent/ExecutorService.java b/concurrent/src/main/java/java/util/concurrent/ExecutorService.java index 757f7cb..ddd77bf 100644 --- a/concurrent/src/main/java/java/util/concurrent/ExecutorService.java +++ b/concurrent/src/main/java/java/util/concurrent/ExecutorService.java @@ -258,7 +258,7 @@ public interface ExecutorService extends Executor { * scheduled for execution */ - <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks) + <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException; /** @@ -288,7 +288,7 @@ public interface ExecutorService extends Executor { * @throws RejectedExecutionException if any task cannot be scheduled * for execution */ - <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks, + <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException; @@ -303,14 +303,14 @@ public interface ExecutorService extends Executor { * @param tasks the collection of 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 NullPointerException if tasks or any element task + * subject to execution is <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 */ - <T> T invokeAny(Collection<Callable<T>> tasks) + <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException; /** @@ -327,15 +327,15 @@ public interface ExecutorService extends Executor { * @param unit the time unit of the timeout argument * @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> + * @throws NullPointerException if tasks, or unit, or any element + * task subject to execution is <tt>null</tt> * @throws TimeoutException if the given timeout elapses before * any task successfully completes * @throws ExecutionException if no task successfully completes * @throws RejectedExecutionException if tasks cannot be scheduled * for execution */ - <T> T invokeAny(Collection<Callable<T>> tasks, + <T> T invokeAny(Collection<? extends 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 a57abe5..96f6c15 100644 --- a/concurrent/src/main/java/java/util/concurrent/Executors.java +++ b/concurrent/src/main/java/java/util/concurrent/Executors.java @@ -13,6 +13,7 @@ import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; import java.security.PrivilegedActionException; import java.security.AccessControlException; +// import sun.security.util.SecurityConstants; // android-removed /** * Factory and utility methods for {@link Executor}, {@link @@ -359,7 +360,7 @@ public class Executors { * @return a callable object * @throws NullPointerException if action null */ - public static Callable<Object> callable(final PrivilegedAction action) { + public static Callable<Object> callable(final PrivilegedAction<?> action) { if (action == null) throw new NullPointerException(); return new Callable<Object>() { @@ -374,7 +375,7 @@ public class Executors { * @return a callable object * @throws NullPointerException if action null */ - public static Callable<Object> callable(final PrivilegedExceptionAction action) { + public static Callable<Object> callable(final PrivilegedExceptionAction<?> action) { if (action == null) throw new NullPointerException(); return new Callable<Object>() { @@ -484,7 +485,7 @@ public class Executors { // 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")); + sm.checkPermission(new RuntimePermission("getContextClassLoader")); // android-changed // Whether setContextClassLoader turns out to be necessary // or not, we fail fast if permission is not available. @@ -565,7 +566,7 @@ public class Executors { // 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")); + sm.checkPermission(new RuntimePermission("getContextClassLoader")); // android-changed // Fail fast sm.checkPermission(new RuntimePermission("setContextClassLoader")); @@ -614,20 +615,20 @@ public class Executors { public <T> Future<T> submit(Runnable task, T result) { return e.submit(task, result); } - public <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks) + public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { return e.invokeAll(tasks); } - public <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks, + public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException { return e.invokeAll(tasks, timeout, unit); } - public <T> T invokeAny(Collection<Callable<T>> tasks) + public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException { return e.invokeAny(tasks); } - public <T> T invokeAny(Collection<Callable<T>> tasks, + public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return e.invokeAny(tasks, timeout, unit); diff --git a/concurrent/src/main/java/java/util/concurrent/FutureTask.java b/concurrent/src/main/java/java/util/concurrent/FutureTask.java index 8aa9dd1..6bd13f9 100644 --- a/concurrent/src/main/java/java/util/concurrent/FutureTask.java +++ b/concurrent/src/main/java/java/util/concurrent/FutureTask.java @@ -30,7 +30,7 @@ import java.util.concurrent.locks.*; * @author Doug Lea * @param <V> The result type returned by this FutureTask's <tt>get</tt> method */ -public class FutureTask<V> implements Future<V>, Runnable { +public class FutureTask<V> implements RunnableFuture<V> { /** Synchronization control for FutureTask */ private final Sync sync; diff --git a/concurrent/src/main/java/java/util/concurrent/Java6Arrays.java b/concurrent/src/main/java/java/util/concurrent/Java6Arrays.java deleted file mode 100644 index 6b728be..0000000 --- a/concurrent/src/main/java/java/util/concurrent/Java6Arrays.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package java.util.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/LinkedBlockingDeque.java b/concurrent/src/main/java/java/util/concurrent/LinkedBlockingDeque.java new file mode 100644 index 0000000..196ea76 --- /dev/null +++ b/concurrent/src/main/java/java/util/concurrent/LinkedBlockingDeque.java @@ -0,0 +1,1137 @@ +/* + * 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; + +import java.util.AbstractQueue; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +/** + * An optionally-bounded {@linkplain BlockingDeque blocking deque} based on + * linked nodes. + * + * <p> The optional capacity bound constructor argument serves as a + * way to prevent excessive expansion. The capacity, if unspecified, + * is equal to {@link Integer#MAX_VALUE}. Linked nodes are + * dynamically created upon each insertion unless this would bring the + * deque above capacity. + * + * <p>Most operations run in constant time (ignoring time spent + * blocking). Exceptions include {@link #remove(Object) remove}, + * {@link #removeFirstOccurrence removeFirstOccurrence}, {@link + * #removeLastOccurrence removeLastOccurrence}, {@link #contains + * contains}, {@link #iterator iterator.remove()}, and the bulk + * operations, all of which run in linear time. + * + * <p>This class and its iterator implement all of the + * <em>optional</em> methods of the {@link Collection} and {@link + * Iterator} interfaces. + * + * <p>This class is a member of the + * <a href="{@docRoot}/../technotes/guides/collections/index.html"> + * Java Collections Framework</a>. + * + * @since 1.6 + * @author Doug Lea + * @param <E> the type of elements held in this collection + */ +public class LinkedBlockingDeque<E> + extends AbstractQueue<E> + implements BlockingDeque<E>, java.io.Serializable { + + /* + * Implemented as a simple doubly-linked list protected by a + * single lock and using conditions to manage blocking. + * + * To implement weakly consistent iterators, it appears we need to + * keep all Nodes 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 jump to "first" (for next links) + * or "last" (for prev links). + */ + + /* + * We have "diamond" multiple interface/abstract class inheritance + * here, and that introduces ambiguities. Often we want the + * BlockingDeque javadoc combined with the AbstractQueue + * implementation, so a lot of method specs are duplicated here. + */ + + private static final long serialVersionUID = -387911632671998426L; + + /** Doubly-linked list node class */ + static final class Node<E> { + /** + * The item, or null if this node has been removed. + */ + E item; + + /** + * One of: + * - the real predecessor Node + * - this Node, meaning the predecessor is tail + * - null, meaning there is no predecessor + */ + Node<E> prev; + + /** + * One of: + * - the real successor Node + * - this Node, meaning the successor is head + * - null, meaning there is no successor + */ + Node<E> next; + + Node(E x, Node<E> p, Node<E> n) { + item = x; + prev = p; + next = n; + } + } + + /** + * Pointer to first node. + * Invariant: (first == null && last == null) || + * (first.prev == null && first.item != null) + */ + transient Node<E> first; + + /** + * Pointer to last node. + * Invariant: (first == null && last == null) || + * (last.next == null && last.item != null) + */ + transient Node<E> last; + + /** Number of items in the deque */ + private transient int count; + + /** Maximum number of items in the deque */ + private final int capacity; + + /** Main lock guarding all access */ + final ReentrantLock lock = new ReentrantLock(); + + /** Condition for waiting takes */ + private final Condition notEmpty = lock.newCondition(); + + /** Condition for waiting puts */ + private final Condition notFull = lock.newCondition(); + + /** + * Creates a {@code LinkedBlockingDeque} with a capacity of + * {@link Integer#MAX_VALUE}. + */ + public LinkedBlockingDeque() { + this(Integer.MAX_VALUE); + } + + /** + * Creates a {@code LinkedBlockingDeque} with the given (fixed) capacity. + * + * @param capacity the capacity of this deque + * @throws IllegalArgumentException if {@code capacity} is less than 1 + */ + public LinkedBlockingDeque(int capacity) { + if (capacity <= 0) throw new IllegalArgumentException(); + this.capacity = capacity; + } + + /** + * Creates a {@code LinkedBlockingDeque} with a capacity of + * {@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 the specified collection or any + * of its elements are null + */ + public LinkedBlockingDeque(Collection<? extends E> c) { + this(Integer.MAX_VALUE); + final ReentrantLock lock = this.lock; + lock.lock(); // Never contended, but necessary for visibility + try { + for (E e : c) { + if (e == null) + throw new NullPointerException(); + if (!linkLast(e)) + throw new IllegalStateException("Deque full"); + } + } finally { + lock.unlock(); + } + } + + + // Basic linking and unlinking operations, called only while holding lock + + /** + * Links e as first element, or returns false if full. + */ + private boolean linkFirst(E e) { + // assert lock.isHeldByCurrentThread(); + if (count >= capacity) + return false; + Node<E> f = first; + Node<E> x = new Node<E>(e, null, f); + first = x; + if (last == null) + last = x; + else + f.prev = x; + ++count; + notEmpty.signal(); + return true; + } + + /** + * Links e as last element, or returns false if full. + */ + private boolean linkLast(E e) { + // assert lock.isHeldByCurrentThread(); + if (count >= capacity) + return false; + Node<E> l = last; + Node<E> x = new Node<E>(e, l, null); + last = x; + if (first == null) + first = x; + else + l.next = x; + ++count; + notEmpty.signal(); + return true; + } + + /** + * Removes and returns first element, or null if empty. + */ + private E unlinkFirst() { + // assert lock.isHeldByCurrentThread(); + Node<E> f = first; + if (f == null) + return null; + Node<E> n = f.next; + E item = f.item; + f.item = null; + f.next = f; // help GC + first = n; + if (n == null) + last = null; + else + n.prev = null; + --count; + notFull.signal(); + return item; + } + + /** + * Removes and returns last element, or null if empty. + */ + private E unlinkLast() { + // assert lock.isHeldByCurrentThread(); + Node<E> l = last; + if (l == null) + return null; + Node<E> p = l.prev; + E item = l.item; + l.item = null; + l.prev = l; // help GC + last = p; + if (p == null) + first = null; + else + p.next = null; + --count; + notFull.signal(); + return item; + } + + /** + * Unlinks x. + */ + void unlink(Node<E> x) { + // assert lock.isHeldByCurrentThread(); + Node<E> p = x.prev; + Node<E> n = x.next; + if (p == null) { + unlinkFirst(); + } else if (n == null) { + unlinkLast(); + } else { + p.next = n; + n.prev = p; + x.item = null; + // Don't mess with x's links. They may still be in use by + // an iterator. + --count; + notFull.signal(); + } + } + + // BlockingDeque methods + + /** + * @throws IllegalStateException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ + public void addFirst(E e) { + if (!offerFirst(e)) + throw new IllegalStateException("Deque full"); + } + + /** + * @throws IllegalStateException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ + public void addLast(E e) { + if (!offerLast(e)) + throw new IllegalStateException("Deque full"); + } + + /** + * @throws NullPointerException {@inheritDoc} + */ + public boolean offerFirst(E e) { + if (e == null) throw new NullPointerException(); + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return linkFirst(e); + } finally { + lock.unlock(); + } + } + + /** + * @throws NullPointerException {@inheritDoc} + */ + public boolean offerLast(E e) { + if (e == null) throw new NullPointerException(); + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return linkLast(e); + } finally { + lock.unlock(); + } + } + + /** + * @throws NullPointerException {@inheritDoc} + * @throws InterruptedException {@inheritDoc} + */ + public void putFirst(E e) throws InterruptedException { + if (e == null) throw new NullPointerException(); + final ReentrantLock lock = this.lock; + lock.lock(); + try { + while (!linkFirst(e)) + notFull.await(); + } finally { + lock.unlock(); + } + } + + /** + * @throws NullPointerException {@inheritDoc} + * @throws InterruptedException {@inheritDoc} + */ + public void putLast(E e) throws InterruptedException { + if (e == null) throw new NullPointerException(); + final ReentrantLock lock = this.lock; + lock.lock(); + try { + while (!linkLast(e)) + notFull.await(); + } finally { + lock.unlock(); + } + } + + /** + * @throws NullPointerException {@inheritDoc} + * @throws InterruptedException {@inheritDoc} + */ + public boolean offerFirst(E e, long timeout, TimeUnit unit) + throws InterruptedException { + if (e == null) throw new NullPointerException(); + long nanos = unit.toNanos(timeout); + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + try { + while (!linkFirst(e)) { + if (nanos <= 0) + return false; + nanos = notFull.awaitNanos(nanos); + } + return true; + } finally { + lock.unlock(); + } + } + + /** + * @throws NullPointerException {@inheritDoc} + * @throws InterruptedException {@inheritDoc} + */ + public boolean offerLast(E e, long timeout, TimeUnit unit) + throws InterruptedException { + if (e == null) throw new NullPointerException(); + long nanos = unit.toNanos(timeout); + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + try { + while (!linkLast(e)) { + if (nanos <= 0) + return false; + nanos = notFull.awaitNanos(nanos); + } + return true; + } finally { + lock.unlock(); + } + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E removeFirst() { + E x = pollFirst(); + if (x == null) throw new NoSuchElementException(); + return x; + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E removeLast() { + E x = pollLast(); + if (x == null) throw new NoSuchElementException(); + return x; + } + + public E pollFirst() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return unlinkFirst(); + } finally { + lock.unlock(); + } + } + + public E pollLast() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return unlinkLast(); + } finally { + lock.unlock(); + } + } + + public E takeFirst() throws InterruptedException { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + E x; + while ( (x = unlinkFirst()) == null) + notEmpty.await(); + return x; + } finally { + lock.unlock(); + } + } + + public E takeLast() throws InterruptedException { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + E x; + while ( (x = unlinkLast()) == null) + notEmpty.await(); + return x; + } finally { + lock.unlock(); + } + } + + public E pollFirst(long timeout, TimeUnit unit) + throws InterruptedException { + long nanos = unit.toNanos(timeout); + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + try { + E x; + while ( (x = unlinkFirst()) == null) { + if (nanos <= 0) + return null; + nanos = notEmpty.awaitNanos(nanos); + } + return x; + } finally { + lock.unlock(); + } + } + + public E pollLast(long timeout, TimeUnit unit) + throws InterruptedException { + long nanos = unit.toNanos(timeout); + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + try { + E x; + while ( (x = unlinkLast()) == null) { + if (nanos <= 0) + return null; + nanos = notEmpty.awaitNanos(nanos); + } + return x; + } finally { + lock.unlock(); + } + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E getFirst() { + E x = peekFirst(); + if (x == null) throw new NoSuchElementException(); + return x; + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E getLast() { + E x = peekLast(); + if (x == null) throw new NoSuchElementException(); + return x; + } + + public E peekFirst() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return (first == null) ? null : first.item; + } finally { + lock.unlock(); + } + } + + public E peekLast() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return (last == null) ? null : last.item; + } finally { + lock.unlock(); + } + } + + public boolean removeFirstOccurrence(Object o) { + if (o == null) return false; + final ReentrantLock lock = this.lock; + lock.lock(); + try { + for (Node<E> p = first; p != null; p = p.next) { + if (o.equals(p.item)) { + unlink(p); + return true; + } + } + return false; + } finally { + lock.unlock(); + } + } + + public boolean removeLastOccurrence(Object o) { + if (o == null) return false; + final ReentrantLock lock = this.lock; + lock.lock(); + try { + for (Node<E> p = last; p != null; p = p.prev) { + if (o.equals(p.item)) { + unlink(p); + return true; + } + } + return false; + } finally { + lock.unlock(); + } + } + + // BlockingQueue methods + + /** + * Inserts the specified element at the end of this deque unless it would + * violate capacity restrictions. When using a capacity-restricted deque, + * it is generally preferable to use method {@link #offer(Object) offer}. + * + * <p>This method is equivalent to {@link #addLast}. + * + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws NullPointerException if the specified element is null + */ + public boolean add(E e) { + addLast(e); + return true; + } + + /** + * @throws NullPointerException if the specified element is null + */ + public boolean offer(E e) { + return offerLast(e); + } + + /** + * @throws NullPointerException {@inheritDoc} + * @throws InterruptedException {@inheritDoc} + */ + public void put(E e) throws InterruptedException { + putLast(e); + } + + /** + * @throws NullPointerException {@inheritDoc} + * @throws InterruptedException {@inheritDoc} + */ + public boolean offer(E e, long timeout, TimeUnit unit) + throws InterruptedException { + return offerLast(e, timeout, unit); + } + + /** + * Retrieves and removes the head of the queue represented by this deque. + * This method differs from {@link #poll poll} only in that it throws an + * exception if this deque is empty. + * + * <p>This method is equivalent to {@link #removeFirst() removeFirst}. + * + * @return the head of the queue represented by this deque + * @throws NoSuchElementException if this deque is empty + */ + public E remove() { + return removeFirst(); + } + + public E poll() { + return pollFirst(); + } + + public E take() throws InterruptedException { + return takeFirst(); + } + + public E poll(long timeout, TimeUnit unit) throws InterruptedException { + return pollFirst(timeout, unit); + } + + /** + * Retrieves, but does not remove, the head of the queue represented by + * this deque. This method differs from {@link #peek peek} only in that + * it throws an exception if this deque is empty. + * + * <p>This method is equivalent to {@link #getFirst() getFirst}. + * + * @return the head of the queue represented by this deque + * @throws NoSuchElementException if this deque is empty + */ + public E element() { + return getFirst(); + } + + public E peek() { + return peekFirst(); + } + + /** + * Returns the number of additional elements that this deque can ideally + * (in the absence of memory or resource constraints) accept without + * blocking. This is always equal to the initial capacity of this deque + * less the current {@code size} of this deque. + * + * <p>Note that you <em>cannot</em> always tell if an attempt to insert + * an element will succeed by inspecting {@code remainingCapacity} + * 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; + lock.lock(); + try { + return capacity - count; + } finally { + lock.unlock(); + } + } + + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + public int drainTo(Collection<? super E> c) { + return drainTo(c, Integer.MAX_VALUE); + } + + /** + * @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(); + final ReentrantLock lock = this.lock; + lock.lock(); + try { + int n = Math.min(maxElements, count); + for (int i = 0; i < n; i++) { + c.add(first.item); // In this order, in case add() throws. + unlinkFirst(); + } + return n; + } finally { + lock.unlock(); + } + } + + // Stack methods + + /** + * @throws IllegalStateException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ + public void push(E e) { + addFirst(e); + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E pop() { + return removeFirst(); + } + + // Collection methods + + /** + * Removes the first occurrence of the specified element from this deque. + * If the deque does not contain the element, it is unchanged. + * More formally, removes the first element {@code e} such that + * {@code o.equals(e)} (if such an element exists). + * Returns {@code true} if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). + * + * <p>This method is equivalent to + * {@link #removeFirstOccurrence(Object) removeFirstOccurrence}. + * + * @param o element to be removed from this deque, if present + * @return {@code true} if this deque changed as a result of the call + */ + public boolean remove(Object o) { + return removeFirstOccurrence(o); + } + + /** + * Returns the number of elements in this deque. + * + * @return the number of elements in this deque + */ + public int size() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return count; + } finally { + lock.unlock(); + } + } + + /** + * Returns {@code true} if this deque contains the specified element. + * More formally, returns {@code true} if and only if this deque contains + * at least one element {@code e} such that {@code o.equals(e)}. + * + * @param o object to be checked for containment in this deque + * @return {@code true} if this deque contains the specified element + */ + public boolean contains(Object o) { + if (o == null) return false; + final ReentrantLock lock = this.lock; + lock.lock(); + try { + for (Node<E> p = first; p != null; p = p.next) + if (o.equals(p.item)) + return true; + return false; + } finally { + lock.unlock(); + } + } + + /* + * TODO: Add support for more efficient bulk operations. + * + * We don't want to acquire the lock for every iteration, but we + * also want other threads a chance to interact with the + * collection, especially when count is close to capacity. + */ + +// /** +// * Adds all of the elements in the specified collection to this +// * queue. Attempts to addAll of a queue to itself result in +// * {@code IllegalArgumentException}. Further, 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 queue +// * @return {@code true} if this queue changed as a result of the call +// * @throws ClassCastException {@inheritDoc} +// * @throws NullPointerException {@inheritDoc} +// * @throws IllegalArgumentException {@inheritDoc} +// * @throws IllegalStateException {@inheritDoc} +// * @see #add(Object) +// */ +// public boolean addAll(Collection<? extends E> c) { +// if (c == null) +// throw new NullPointerException(); +// if (c == this) +// throw new IllegalArgumentException(); +// final ReentrantLock lock = this.lock; +// lock.lock(); +// try { +// boolean modified = false; +// for (E e : c) +// if (linkLast(e)) +// modified = true; +// return modified; +// } finally { +// lock.unlock(); +// } +// } + + /** + * Returns an array containing all of the elements in this deque, 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 deque. (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 deque + */ + @SuppressWarnings("unchecked") + public Object[] toArray() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] a = new Object[count]; + int k = 0; + for (Node<E> p = first; p != null; p = p.next) + a[k++] = p.item; + return a; + } finally { + lock.unlock(); + } + } + + /** + * Returns an array containing all of the elements in this deque, in + * proper sequence; the runtime type of the returned array is that of + * the specified array. If the deque 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 deque. + * + * <p>If this deque fits in the specified array with room to spare + * (i.e., the array has more elements than this deque), the element in + * the array immediately following the end of the deque 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 deque known to contain only strings. + * The following code can be used to dump the deque 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 deque 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 deque + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this deque + * @throws NullPointerException if the specified array is null + */ + @SuppressWarnings("unchecked") + public <T> T[] toArray(T[] a) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + if (a.length < count) + a = (T[])java.lang.reflect.Array.newInstance + (a.getClass().getComponentType(), count); + + int k = 0; + for (Node<E> p = first; p != null; p = p.next) + a[k++] = (T)p.item; + if (a.length > k) + a[k] = null; + return a; + } finally { + lock.unlock(); + } + } + + public String toString() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return super.toString(); + } finally { + lock.unlock(); + } + } + + /** + * Atomically removes all of the elements from this deque. + * The deque will be empty after this call returns. + */ + public void clear() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + for (Node<E> f = first; f != null; ) { + f.item = null; + Node<E> n = f.next; + f.prev = null; + f.next = null; + f = n; + } + first = last = null; + count = 0; + notFull.signalAll(); + } finally { + lock.unlock(); + } + } + + /** + * Returns an iterator over the elements in this deque in proper sequence. + * The elements will be returned in order from first (head) to last (tail). + * The returned {@code Iterator} is a "weakly consistent" iterator that + * will never throw {@link java.util.ConcurrentModificationException + * 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 deque in proper sequence + */ + public Iterator<E> iterator() { + return new Itr(); + } + + /** + * Returns an iterator over the elements in this deque in reverse + * sequential order. The elements will be returned in order from + * last (tail) to first (head). + * The returned {@code Iterator} is a "weakly consistent" iterator that + * will never throw {@link java.util.ConcurrentModificationException + * ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + */ + public Iterator<E> descendingIterator() { + return new DescendingItr(); + } + + /** + * Base class for Iterators for LinkedBlockingDeque + */ + private abstract class AbstractItr implements Iterator<E> { + /** + * The next node to return in next() + */ + Node<E> next; + + /** + * nextItem holds on to item fields because once we claim that + * an element exists in hasNext(), we must return item read + * under lock (in advance()) even if it was in the process of + * being removed when hasNext() was called. + */ + E nextItem; + + /** + * Node returned by most recent call to next. Needed by remove. + * Reset to null if this element is deleted by a call to remove. + */ + private Node<E> lastRet; + + abstract Node<E> firstNode(); + abstract Node<E> nextNode(Node<E> n); + + AbstractItr() { + // set to initial position + final ReentrantLock lock = LinkedBlockingDeque.this.lock; + lock.lock(); + try { + next = firstNode(); + nextItem = (next == null) ? null : next.item; + } finally { + lock.unlock(); + } + } + + /** + * Advances next. + */ + void advance() { + final ReentrantLock lock = LinkedBlockingDeque.this.lock; + lock.lock(); + try { + // assert next != null; + Node<E> s = nextNode(next); + if (s == next) { + next = firstNode(); + } else { + // Skip over removed nodes. + // May be necessary if multiple interior Nodes are removed. + while (s != null && s.item == null) + s = nextNode(s); + next = s; + } + nextItem = (next == null) ? null : next.item; + } finally { + lock.unlock(); + } + } + + public boolean hasNext() { + return next != null; + } + + public E next() { + if (next == null) + throw new NoSuchElementException(); + lastRet = next; + E x = nextItem; + advance(); + return x; + } + + public void remove() { + Node<E> n = lastRet; + if (n == null) + throw new IllegalStateException(); + lastRet = null; + final ReentrantLock lock = LinkedBlockingDeque.this.lock; + lock.lock(); + try { + if (n.item != null) + unlink(n); + } finally { + lock.unlock(); + } + } + } + + /** Forward iterator */ + private class Itr extends AbstractItr { + Node<E> firstNode() { return first; } + Node<E> nextNode(Node<E> n) { return n.next; } + } + + /** Descending iterator */ + private class DescendingItr extends AbstractItr { + Node<E> firstNode() { return last; } + Node<E> nextNode(Node<E> n) { return n.prev; } + } + + /** + * Save the state of this deque to a stream (that is, serialize it). + * + * @serialData The capacity (int), followed by elements (each an + * {@code Object}) in the proper order, followed by a null + * @param s the stream + */ + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + // Write out capacity and any hidden stuff + s.defaultWriteObject(); + // Write out all elements in the proper order. + for (Node<E> p = first; p != null; p = p.next) + s.writeObject(p.item); + // Use trailing null as sentinel + s.writeObject(null); + } finally { + lock.unlock(); + } + } + + /** + * Reconstitute this deque from a stream (that is, + * deserialize it). + * @param s the stream + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + count = 0; + first = null; + last = null; + // Read in all elements and place in queue + for (;;) { + @SuppressWarnings("unchecked") + E item = (E)s.readObject(); + if (item == null) + break; + add(item); + } + } + +} diff --git a/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java b/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java index e06f7bd..1a8e412 100644 --- a/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java +++ b/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java @@ -5,14 +5,19 @@ */ package java.util.concurrent; -import java.util.concurrent.atomic.*; -import java.util.concurrent.locks.*; -import java.util.*; // BEGIN android-note // removed link to collections framework docs // END android-note +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; +import java.util.AbstractQueue; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; + /** * An optionally-bounded {@linkplain BlockingQueue blocking queue} based on * linked nodes. @@ -57,15 +62,43 @@ 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. + * + * Visibility between writers and readers is provided as follows: + * + * Whenever an element is enqueued, the putLock is acquired and + * count updated. A subsequent reader guarantees visibility to the + * enqueued Node by either acquiring the putLock (via fullyLock) + * or by acquiring the takeLock, and then reading n = count.get(); + * this gives visibility to the first n items. + * + * To implement weakly consistent iterators, it appears we need to + * keep all Nodes 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.next. */ /** * Linked list node class */ static class Node<E> { - /** The item, volatile to ensure barrier separating write and read */ - volatile E item; + E item; + + /** + * One of: + * - the real successor Node + * - this Node, meaning the successor is head.next + * - null, meaning there is no successor (this is the last node) + */ Node<E> next; + Node(E x) { item = x; } } @@ -75,10 +108,16 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> /** Current number of elements */ private final AtomicInteger count = new AtomicInteger(0); - /** Head of linked list */ + /** + * Head of linked list. + * Invariant: head.item == null + */ private transient Node<E> head; - /** Tail of linked list */ + /** + * Tail of linked list. + * Invariant: last.next == null + */ private transient Node<E> last; /** Lock held by take, poll, etc */ @@ -122,20 +161,26 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> /** * Creates a node and links it at end of queue. + * * @param x the item */ - private void insert(E x) { + private void enqueue(E x) { + // assert putLock.isHeldByCurrentThread(); + // assert last.next == null; last = last.next = new Node<E>(x); } /** - * Removes a node from head of queue, + * Removes a node from head of queue. + * * @return the node */ - private E extract() { + private E dequeue() { + // assert takeLock.isHeldByCurrentThread(); + // assert head.item == null; Node<E> h = head; Node<E> first = h.next; - h.next = null; // help GC + h.next = h; // help GC head = first; E x = first.item; first.item = null; @@ -145,7 +190,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> /** * Lock to prevent both puts and takes. */ - private void fullyLock() { + void fullyLock() { putLock.lock(); takeLock.lock(); } @@ -153,14 +198,21 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> /** * Unlock to allow both puts and takes. */ - private void fullyUnlock() { + void fullyUnlock() { takeLock.unlock(); putLock.unlock(); } +// /** +// * Tells whether both locks are held by current thread. +// */ +// boolean isFullyLocked() { +// return (putLock.isHeldByCurrentThread() && +// takeLock.isHeldByCurrentThread()); +// } /** - * Creates a <tt>LinkedBlockingQueue</tt> with a capacity of + * Creates a {@code LinkedBlockingQueue} with a capacity of * {@link Integer#MAX_VALUE}. */ public LinkedBlockingQueue() { @@ -168,10 +220,10 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } /** - * Creates a <tt>LinkedBlockingQueue</tt> with the given (fixed) capacity. + * Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity. * * @param capacity the capacity of this queue - * @throws IllegalArgumentException if <tt>capacity</tt> is not greater + * @throws IllegalArgumentException if {@code capacity} is not greater * than zero */ public LinkedBlockingQueue(int capacity) { @@ -181,7 +233,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } /** - * Creates a <tt>LinkedBlockingQueue</tt> with a capacity of + * Creates a {@code LinkedBlockingQueue} with a capacity of * {@link Integer#MAX_VALUE}, initially containing the elements of the * given collection, * added in traversal order of the collection's iterator. @@ -192,8 +244,22 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> */ public LinkedBlockingQueue(Collection<? extends E> c) { this(Integer.MAX_VALUE); - for (E e : c) - add(e); + final ReentrantLock putLock = this.putLock; + putLock.lock(); // Never contended, but necessary for visibility + try { + int n = 0; + for (E e : c) { + if (e == null) + throw new NullPointerException(); + if (n == capacity) + throw new IllegalStateException("Queue full"); + enqueue(e); + ++n; + } + count.set(n); + } finally { + putLock.unlock(); + } } @@ -214,10 +280,10 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> * 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. + * less the current {@code size} of this queue. * * <p>Note that you <em>cannot</em> always tell if an attempt to insert - * an element will succeed by inspecting <tt>remainingCapacity</tt> + * an element will succeed by inspecting {@code remainingCapacity} * because it may be the case that another thread is about to * insert or remove an element. */ @@ -234,8 +300,8 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> */ 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. + // Note: convention in all put/take/etc is to preset local var + // holding count negative to indicate failure unless set. int c = -1; final ReentrantLock putLock = this.putLock; final AtomicInteger count = this.count; @@ -246,18 +312,13 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> * not protected by lock. This works because count can * only decrease at this point (all other puts are shut * out by lock), and we (or some other waiting put) are - * signalled if it ever changes from - * capacity. Similarly for all other uses of count in - * other wait guards. + * signalled if it ever changes from capacity. Similarly + * for all other uses of count in other wait guards. */ - try { - while (count.get() == capacity) - notFull.await(); - } catch (InterruptedException ie) { - notFull.signal(); // propagate to a non-interrupted thread - throw ie; + while (count.get() == capacity) { + notFull.await(); } - insert(e); + enqueue(e); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); @@ -272,7 +333,7 @@ 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. * - * @return <tt>true</tt> if successful, or <tt>false</tt> if + * @return {@code true} if successful, or {@code false} if * the specified waiting time elapses before space is available. * @throws InterruptedException {@inheritDoc} * @throws NullPointerException {@inheritDoc} @@ -287,23 +348,15 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> final AtomicInteger count = this.count; putLock.lockInterruptibly(); try { - for (;;) { - if (count.get() < capacity) { - insert(e); - c = count.getAndIncrement(); - if (c + 1 < capacity) - notFull.signal(); - break; - } + while (count.get() == capacity) { if (nanos <= 0) return false; - try { - nanos = notFull.awaitNanos(nanos); - } catch (InterruptedException ie) { - notFull.signal(); // propagate to a non-interrupted thread - throw ie; - } + nanos = notFull.awaitNanos(nanos); } + enqueue(e); + c = count.getAndIncrement(); + if (c + 1 < capacity) + notFull.signal(); } finally { putLock.unlock(); } @@ -315,7 +368,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<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 + * returning {@code true} upon success and {@code false} 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 @@ -333,7 +386,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> putLock.lock(); try { if (count.get() < capacity) { - insert(e); + enqueue(e); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); @@ -354,15 +407,10 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> final ReentrantLock takeLock = this.takeLock; takeLock.lockInterruptibly(); try { - try { - while (count.get() == 0) - notEmpty.await(); - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to a non-interrupted thread - throw ie; + while (count.get() == 0) { + notEmpty.await(); } - - x = extract(); + x = dequeue(); c = count.getAndDecrement(); if (c > 1) notEmpty.signal(); @@ -382,23 +430,15 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> final ReentrantLock takeLock = this.takeLock; takeLock.lockInterruptibly(); try { - for (;;) { - if (count.get() > 0) { - x = extract(); - c = count.getAndDecrement(); - if (c > 1) - notEmpty.signal(); - break; - } + while (count.get() == 0) { if (nanos <= 0) return null; - try { - nanos = notEmpty.awaitNanos(nanos); - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to a non-interrupted thread - throw ie; - } + nanos = notEmpty.awaitNanos(nanos); } + x = dequeue(); + c = count.getAndDecrement(); + if (c > 1) + notEmpty.signal(); } finally { takeLock.unlock(); } @@ -417,7 +457,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> takeLock.lock(); try { if (count.get() > 0) { - x = extract(); + x = dequeue(); c = count.getAndDecrement(); if (c > 1) notEmpty.signal(); @@ -430,7 +470,6 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> return x; } - public E peek() { if (count.get() == 0) return null; @@ -448,43 +487,47 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } /** + * Unlinks interior Node p with predecessor trail. + */ + void unlink(Node<E> p, Node<E> trail) { + // assert isFullyLocked(); + // p.next is not changed, to allow iterators that are + // traversing p to maintain their weak-consistency guarantee. + p.item = null; + trail.next = p.next; + if (last == p) + last = trail; + if (count.getAndDecrement() == capacity) + notFull.signal(); + } + + /** * 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 + * 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 <tt>true</tt> if this queue contained the specified element + * 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 <tt>true</tt> if this queue changed as a result of the call + * @return {@code true} if this queue changed as a result of the call */ public boolean remove(Object o) { if (o == null) return false; - boolean removed = false; fullyLock(); try { - Node<E> trail = head; - Node<E> p = head.next; - while (p != null) { + for (Node<E> trail = head, p = trail.next; + p != null; + trail = p, p = p.next) { if (o.equals(p.item)) { - removed = true; - break; + unlink(p, trail); + return true; } - trail = p; - p = p.next; - } - if (removed) { - p.item = null; - trail.next = p.next; - if (last == p) - last = trail; - if (count.getAndDecrement() == capacity) - notFull.signalAll(); } + return false; } finally { fullyUnlock(); } - return removed; } /** @@ -524,22 +567,22 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> * <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>. + * {@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 <tt>x</tt> is a queue known to contain only strings. + * <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 <tt>String</tt>: + * allocated array of {@code String}: * * <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>. + * 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 @@ -550,6 +593,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> * this queue * @throws NullPointerException if the specified array is null */ + @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { fullyLock(); try { @@ -559,7 +603,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> (a.getClass().getComponentType(), size); int k = 0; - for (Node p = head.next; p != null; p = p.next) + for (Node<E> p = head.next; p != null; p = p.next) a[k++] = (T)p.item; if (a.length > k) a[k] = null; @@ -585,11 +629,14 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> public void clear() { fullyLock(); try { - head.next = null; - assert head.item == null; - last = head; + for (Node<E> p, h = head; (p = h.next) != null; h = p) { + h.next = h; + p.item = null; + } + head = last; + // assert head.item == null && head.next == null; if (count.getAndSet(0) == capacity) - notFull.signalAll(); + notFull.signal(); } finally { fullyUnlock(); } @@ -602,30 +649,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> * @throws IllegalArgumentException {@inheritDoc} */ public int drainTo(Collection<? super E> c) { - if (c == null) - throw new NullPointerException(); - if (c == this) - throw new IllegalArgumentException(); - 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 { - fullyUnlock(); - } - // Transfer the elements outside of locks - int n = 0; - for (Node<E> p = first; p != null; p = p.next) { - c.add(p.item); - p.item = null; - ++n; - } - return n; + return drainTo(c, Integer.MAX_VALUE); } /** @@ -639,34 +663,44 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> throw new NullPointerException(); if (c == this) throw new IllegalArgumentException(); - fullyLock(); + boolean signalNotFull = false; + final ReentrantLock takeLock = this.takeLock; + takeLock.lock(); try { - int n = 0; - Node<E> p = head.next; - while (p != null && n < maxElements) { - c.add(p.item); - p.item = null; - p = p.next; - ++n; - } - if (n != 0) { - head.next = p; - assert head.item == null; - if (p == null) - last = head; - if (count.getAndAdd(-n) == capacity) - notFull.signalAll(); + int n = Math.min(maxElements, count.get()); + // count.get provides visibility to first n Nodes + Node<E> h = head; + int i = 0; + try { + while (i < n) { + Node<E> p = h.next; + c.add(p.item); + p.item = null; + h.next = h; + h = p; + ++i; + } + return n; + } finally { + // Restore invariants even if c.add() threw + if (i > 0) { + // assert h.item == null; + head = h; + signalNotFull = (count.getAndAdd(-i) == capacity); + } } - return n; } finally { - fullyUnlock(); + takeLock.unlock(); + if (signalNotFull) + signalNotFull(); } } /** * 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 ConcurrentModificationException}, + * The returned {@code Iterator} is a "weakly consistent" iterator that + * will never throw {@link java.util.ConcurrentModificationException + * 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. @@ -679,7 +713,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> private class Itr implements Iterator<E> { /* - * Basic weak-consistent iterator. At all times hold the next + * Basic weakly-consistent iterator. At all times hold the next * item to hand out so that if hasNext() reports true, we will * still have it to return even if lost race with a take etc. */ @@ -688,17 +722,13 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> private E currentElement; Itr() { - final ReentrantLock putLock = LinkedBlockingQueue.this.putLock; - final ReentrantLock takeLock = LinkedBlockingQueue.this.takeLock; - putLock.lock(); - takeLock.lock(); + fullyLock(); try { current = head.next; if (current != null) currentElement = current.item; } finally { - takeLock.unlock(); - putLock.unlock(); + fullyUnlock(); } } @@ -706,54 +736,56 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> return current != null; } + /** + * Returns the next live successor of p, or null if no such. + * + * Unlike other traversal methods, iterators need to handle both: + * - dequeued nodes (p.next == p) + * - (possibly multiple) interior removed nodes (p.item == null) + */ + private Node<E> nextNode(Node<E> p) { + for (;;) { + Node<E> s = p.next; + if (s == p) + return head.next; + if (s == null || s.item != null) + return s; + p = s; + } + } + public E next() { - final ReentrantLock putLock = LinkedBlockingQueue.this.putLock; - final ReentrantLock takeLock = LinkedBlockingQueue.this.takeLock; - putLock.lock(); - takeLock.lock(); + fullyLock(); try { if (current == null) throw new NoSuchElementException(); E x = currentElement; lastRet = current; - current = current.next; - if (current != null) - currentElement = current.item; + current = nextNode(current); + currentElement = (current == null) ? null : current.item; return x; } finally { - takeLock.unlock(); - putLock.unlock(); + fullyUnlock(); } } public void remove() { if (lastRet == null) throw new IllegalStateException(); - final ReentrantLock putLock = LinkedBlockingQueue.this.putLock; - final ReentrantLock takeLock = LinkedBlockingQueue.this.takeLock; - putLock.lock(); - takeLock.lock(); + fullyLock(); try { Node<E> node = lastRet; lastRet = null; - Node<E> trail = head; - Node<E> p = head.next; - while (p != null && p != node) { - trail = p; - p = p.next; - } - if (p == node) { - p.item = null; - trail.next = p.next; - if (last == p) - last = trail; - int c = count.getAndDecrement(); - if (c == capacity) - notFull.signalAll(); + for (Node<E> trail = head, p = trail.next; + p != null; + trail = p, p = p.next) { + if (p == node) { + unlink(p, trail); + break; + } } } finally { - takeLock.unlock(); - putLock.unlock(); + fullyUnlock(); } } } @@ -762,7 +794,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> * Save the state to a stream (that is, serialize it). * * @serialData The capacity is emitted (int), followed by all of - * its elements (each an <tt>Object</tt>) in the proper order, + * its elements (each an {@code Object}) in the proper order, * followed by a null * @param s the stream */ @@ -788,6 +820,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> /** * Reconstitute this queue instance from a stream (that is, * deserialize it). + * * @param s the stream */ private void readObject(java.io.ObjectInputStream s) @@ -800,6 +833,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> // 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/PriorityBlockingQueue.java b/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java index 7a33dc7..35d7b89 100644 --- a/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java +++ b/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java @@ -146,7 +146,6 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> return offer(e); } - // BEGIN android-changed /** * Inserts the specified element into this priority queue. * diff --git a/concurrent/src/main/java/java/util/concurrent/RunnableFuture.java b/concurrent/src/main/java/java/util/concurrent/RunnableFuture.java new file mode 100644 index 0000000..d74211d --- /dev/null +++ b/concurrent/src/main/java/java/util/concurrent/RunnableFuture.java @@ -0,0 +1,25 @@ +/* + * 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; + +/** + * A {@link Future} that is {@link Runnable}. Successful execution of + * the <tt>run</tt> method causes completion of the <tt>Future</tt> + * and allows access to its results. + * @see FutureTask + * @see Executor + * @since 1.6 + * @author Doug Lea + * @param <V> The result type returned by this Future's <tt>get</tt> method + */ +public interface RunnableFuture<V> extends Runnable, Future<V> { + /** + * Sets this Future to the result of its computation + * unless it has been cancelled. + */ + void run(); +} diff --git a/concurrent/src/main/java/java/util/concurrent/RunnableScheduledFuture.java b/concurrent/src/main/java/java/util/concurrent/RunnableScheduledFuture.java new file mode 100644 index 0000000..0e8cc32 --- /dev/null +++ b/concurrent/src/main/java/java/util/concurrent/RunnableScheduledFuture.java @@ -0,0 +1,29 @@ +/* + * 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; + +/** + * A {@link ScheduledFuture} that is {@link Runnable}. Successful + * execution of the <tt>run</tt> method causes completion of the + * <tt>Future</tt> and allows access to its results. + * @see FutureTask + * @see Executor + * @since 1.6 + * @author Doug Lea + * @param <V> The result type returned by this Future's <tt>get</tt> method + */ +public interface RunnableScheduledFuture<V> extends RunnableFuture<V>, ScheduledFuture<V> { + + /** + * Returns true if this is a periodic task. A periodic task may + * re-run according to some schedule. A non-periodic task can be + * run only once. + * + * @return true if this task is periodic + */ + boolean isPeriodic(); +} diff --git a/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java b/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java index 83ffb99..7e69936 100644 --- a/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java +++ b/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java @@ -4,10 +4,6 @@ * http://creativecommons.org/licenses/publicdomain */ -/* - * Modified in Apache Harmony. - */ - package java.util.concurrent; import java.util.concurrent.atomic.*; import java.util.concurrent.locks.*; @@ -27,6 +23,15 @@ import java.util.*; * execution time are enabled in first-in-first-out (FIFO) order of * submission. * + * <p>When a submitted task is cancelled before it is run, execution + * is suppressed. By default, such a cancelled task is not + * automatically removed from the work queue until its delay + * elapses. While this enables further inspection and monitoring, it + * may also cause unbounded retention of cancelled tasks. To avoid + * this, set {@link #setRemoveOnCancelPolicy} to {@code true}, which + * causes tasks to be immediately removed from the work queue at + * time of cancellation. + * * <p>Successive executions of a task scheduled via * <code>scheduleAtFixedRate</code> or * <code>scheduleWithFixedDelay</code> do not overlap. While different @@ -37,9 +42,47 @@ import java.util.*; * * <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 {@code - * corePoolSize} threads and an unbounded queue, adjustments to {@code - * maximumPoolSize} 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. Additionally, it + * is almost never a good idea to set {@code corePoolSize} to zero or + * use {@code allowCoreThreadTimeOut} because this may leave the pool + * without threads to handle tasks once they become eligible to run. + * + * <p><b>Extension notes:</b> This class overrides the + * {@link ThreadPoolExecutor#execute execute} and + * {@link AbstractExecutorService#submit(Runnable) submit} + * methods to generate internal {@link ScheduledFuture} objects to + * control per-task delays and scheduling. To preserve + * functionality, any further overrides of these methods in + * subclasses must invoke superclass versions, which effectively + * disables additional task customization. However, this class + * provides alternative protected extension method + * {@code decorateTask} (one version each for {@code Runnable} and + * {@code Callable}) that can be used to customize the concrete task + * types used to execute commands entered via {@code execute}, + * {@code submit}, {@code schedule}, {@code scheduleAtFixedRate}, + * and {@code scheduleWithFixedDelay}. By default, a + * {@code ScheduledThreadPoolExecutor} uses a task type extending + * {@link FutureTask}. However, this may be modified or replaced using + * subclasses of the form: + * + * <pre> {@code + * public class CustomScheduledExecutor extends ScheduledThreadPoolExecutor { + * + * static class CustomTask<V> implements RunnableScheduledFuture<V> { ... } + * + * protected <V> RunnableScheduledFuture<V> decorateTask( + * Runnable r, RunnableScheduledFuture<V> task) { + * return new CustomTask<V>(r, task); + * } + * + * protected <V> RunnableScheduledFuture<V> decorateTask( + * Callable<V> c, RunnableScheduledFuture<V> task) { + * return new CustomTask<V>(c, task); + * } + * // ... add constructors, etc. + * }}</pre> * * @since 1.5 * @author Doug Lea @@ -96,23 +139,15 @@ public class ScheduledThreadPoolExecutor */ private static final AtomicLong sequencer = new AtomicLong(0); - - /** - * 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. - */ - static final long initialNanoTime = System.nanoTime(); - /** * Returns current nanosecond time. */ - static long now() { - return System.nanoTime() - initialNanoTime; + final long now() { + return System.nanoTime(); } private class ScheduledFutureTask<V> - extends FutureTask<V> implements ScheduledFuture<V> { + extends FutureTask<V> implements RunnableScheduledFuture<V> { /** Sequence number to break ties FIFO */ private final long sequenceNumber; @@ -129,7 +164,7 @@ public class ScheduledThreadPoolExecutor private final long period; /** The actual task to be re-enqueued by reExecutePeriodic */ - ScheduledFutureTask<V> outerTask = this; + RunnableScheduledFuture<V> outerTask = this; /** * Index into delay queue, to support faster cancellation. @@ -167,8 +202,7 @@ public class ScheduledThreadPoolExecutor } public long getDelay(TimeUnit unit) { - long d = time - now(); - return d<=0? 0 : unit.convert(d, TimeUnit.NANOSECONDS); + return unit.convert(time - now(), TimeUnit.NANOSECONDS); } public int compareTo(Delayed other) { @@ -208,7 +242,7 @@ public class ScheduledThreadPoolExecutor if (p > 0) time += p; else - time = now() - p; + time = triggerTime(-p); } public boolean cancel(boolean mayInterruptIfRunning) { @@ -257,7 +291,7 @@ public class ScheduledThreadPoolExecutor * * @param task the task */ - private void delayedExecute(ScheduledFutureTask<?> task) { + private void delayedExecute(RunnableScheduledFuture<?> task) { if (isShutdown()) reject(task); else { @@ -277,7 +311,7 @@ public class ScheduledThreadPoolExecutor * * @param task the task */ - void reExecutePeriodic(ScheduledFutureTask<?> task) { + void reExecutePeriodic(RunnableScheduledFuture<?> task) { if (canRunInCurrentRunState(true)) { super.getQueue().add(task); if (!canRunInCurrentRunState(true) && remove(task)) @@ -302,9 +336,9 @@ public class ScheduledThreadPoolExecutor else { // Traverse snapshot to avoid iterator exceptions for (Object e : q.toArray()) { - if (e instanceof ScheduledFutureTask) { - ScheduledFutureTask<?> t = - (ScheduledFutureTask<?>)e; + if (e instanceof RunnableScheduledFuture) { + RunnableScheduledFuture<?> t = + (RunnableScheduledFuture<?>)e; if ((t.isPeriodic() ? !keepPeriodic : !keepDelayed) || t.isCancelled()) { // also remove if already cancelled if (q.remove(t)) @@ -317,11 +351,43 @@ public class ScheduledThreadPoolExecutor } /** + * Modifies or replaces the task used to execute a runnable. + * This method can be used to override the concrete + * class used for managing internal tasks. + * The default implementation simply returns the given task. + * + * @param runnable the submitted Runnable + * @param task the task created to execute the runnable + * @return a task that can execute the runnable + * @since 1.6 + */ + protected <V> RunnableScheduledFuture<V> decorateTask( + Runnable runnable, RunnableScheduledFuture<V> task) { + return task; + } + + /** + * Modifies or replaces the task used to execute a callable. + * This method can be used to override the concrete + * class used for managing internal tasks. + * The default implementation simply returns the given task. + * + * @param callable the submitted Callable + * @param task the task created to execute the callable + * @return a task that can execute the callable + * @since 1.6 + */ + protected <V> RunnableScheduledFuture<V> decorateTask( + Callable<V> callable, RunnableScheduledFuture<V> task) { + return task; + } + + /** * 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 + * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @throws IllegalArgumentException if {@code corePoolSize < 0} */ public ScheduledThreadPoolExecutor(int corePoolSize) { @@ -334,7 +400,7 @@ public class ScheduledThreadPoolExecutor * given initial parameters. * * @param corePoolSize the number of threads to keep in the pool, even - * if they are idle + * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param threadFactory the factory to use when the executor * creates a new thread * @throws IllegalArgumentException if {@code corePoolSize < 0} @@ -351,7 +417,7 @@ public class ScheduledThreadPoolExecutor * initial parameters. * * @param corePoolSize the number of threads to keep in the pool, even - * if they are idle + * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param handler the handler to use when execution is blocked * because the thread bounds and queue capacities are reached * @throws IllegalArgumentException if {@code corePoolSize < 0} @@ -368,7 +434,7 @@ public class ScheduledThreadPoolExecutor * initial parameters. * * @param corePoolSize the number of threads to keep in the pool, even - * if they are idle + * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param threadFactory the factory to use when the executor * creates a new thread * @param handler the handler to use when execution is blocked @@ -385,17 +451,35 @@ public class ScheduledThreadPoolExecutor } /** - * Returns the trigger time of a delayed action + * 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; + private long triggerTime(long delay, TimeUnit unit) { + return triggerTime(unit.toNanos((delay < 0) ? 0 : delay)); + } + + /** + * Returns the trigger time of a delayed action. + */ + long triggerTime(long delay) { + return now() + + ((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay)); + } + + /** + * Constrains the values of all delays in the queue to be within + * Long.MAX_VALUE of each other, to avoid overflow in compareTo. + * This may occur if a task is eligible to be dequeued, but has + * not yet been, while some other task is added with a delay of + * Long.MAX_VALUE. + */ + private long overflowFree(long delay) { + Delayed head = (Delayed) super.getQueue().peek(); + if (head != null) { + long headDelay = head.getDelay(TimeUnit.NANOSECONDS); + if (headDelay < 0 && (delay - headDelay < 0)) + delay = Long.MAX_VALUE + headDelay; + } + return delay; } /** @@ -407,9 +491,9 @@ public class ScheduledThreadPoolExecutor TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); - long triggerTime = nextTriggerTime(delay, unit); - ScheduledFutureTask<?> t - = new ScheduledFutureTask<Void>(command, null, triggerTime); + RunnableScheduledFuture<?> t = decorateTask(command, + new ScheduledFutureTask<Void>(command, null, + triggerTime(delay, unit))); delayedExecute(t); return t; } @@ -423,9 +507,9 @@ public class ScheduledThreadPoolExecutor TimeUnit unit) { if (callable == null || unit == null) throw new NullPointerException(); - long triggerTime = nextTriggerTime(delay, unit); - ScheduledFutureTask<V> t - = new ScheduledFutureTask<V>(callable, triggerTime); + RunnableScheduledFuture<V> t = decorateTask(callable, + new ScheduledFutureTask<V>(callable, + triggerTime(delay, unit))); delayedExecute(t); return t; } @@ -443,16 +527,15 @@ public class ScheduledThreadPoolExecutor throw new NullPointerException(); if (period <= 0) throw new IllegalArgumentException(); - if (initialDelay < 0) initialDelay = 0; - long triggerTime = nextTriggerTime(initialDelay, unit); ScheduledFutureTask<Void> sft = new ScheduledFutureTask<Void>(command, null, - triggerTime, + triggerTime(initialDelay, unit), unit.toNanos(period)); - sft.outerTask = sft; - delayedExecute(sft); - return sft; + RunnableScheduledFuture<Void> t = decorateTask(command, sft); + sft.outerTask = t; + delayedExecute(t); + return t; } /** @@ -468,15 +551,15 @@ public class ScheduledThreadPoolExecutor throw new NullPointerException(); if (delay <= 0) throw new IllegalArgumentException(); - long triggerTime = nextTriggerTime(initialDelay, unit); ScheduledFutureTask<Void> sft = new ScheduledFutureTask<Void>(command, null, - triggerTime, + triggerTime(initialDelay, unit), unit.toNanos(-delay)); - sft.outerTask = sft; - delayedExecute(sft); - return sft; + RunnableScheduledFuture<Void> t = decorateTask(command, sft); + sft.outerTask = t; + delayedExecute(t); + return t; } /** @@ -595,6 +678,33 @@ public class ScheduledThreadPoolExecutor } /** + * Sets the policy on whether cancelled tasks should be immediately + * removed from the work queue at time of cancellation. This value is + * by default {@code false}. + * + * @param value if {@code true}, remove on cancellation, else don't + * @see #getRemoveOnCancelPolicy + * @since 1.7 + */ + /*public*/ void setRemoveOnCancelPolicy(boolean value) { // android-changed + removeOnCancel = value; + } + + /** + * Gets the policy on whether cancelled tasks should be immediately + * removed from the work queue at time of cancellation. This value is + * by default {@code false}. + * + * @return {@code true} if cancelled tasks are immediately removed + * from the queue + * @see #setRemoveOnCancelPolicy + * @since 1.7 + */ + /*public*/ boolean getRemoveOnCancelPolicy() { // android-changed + return removeOnCancel; + } + + /** * 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. @@ -688,8 +798,8 @@ public class ScheduledThreadPoolExecutor */ private static final int INITIAL_CAPACITY = 16; - private ScheduledFutureTask[] queue = - new ScheduledFutureTask[INITIAL_CAPACITY]; + private RunnableScheduledFuture[] queue = + new RunnableScheduledFuture[INITIAL_CAPACITY]; private final ReentrantLock lock = new ReentrantLock(); private int size = 0; @@ -720,7 +830,7 @@ public class ScheduledThreadPoolExecutor /** * Set f's heapIndex if it is a ScheduledFutureTask. */ - private void setIndex(ScheduledFutureTask f, int idx) { + private void setIndex(RunnableScheduledFuture f, int idx) { if (f instanceof ScheduledFutureTask) ((ScheduledFutureTask)f).heapIndex = idx; } @@ -729,10 +839,10 @@ public class ScheduledThreadPoolExecutor * Sift element added at bottom up to its heap-ordered spot. * Call only when holding lock. */ - private void siftUp(int k, ScheduledFutureTask key) { + private void siftUp(int k, RunnableScheduledFuture key) { while (k > 0) { int parent = (k - 1) >>> 1; - ScheduledFutureTask e = queue[parent]; + RunnableScheduledFuture e = queue[parent]; if (key.compareTo(e) >= 0) break; queue[k] = e; @@ -747,11 +857,11 @@ public class ScheduledThreadPoolExecutor * Sift element added at top down to its heap-ordered spot. * Call only when holding lock. */ - private void siftDown(int k, ScheduledFutureTask key) { + private void siftDown(int k, RunnableScheduledFuture key) { int half = size >>> 1; while (k < half) { int child = (k << 1) + 1; - ScheduledFutureTask c = queue[child]; + RunnableScheduledFuture c = queue[child]; int right = child + 1; if (right < size && c.compareTo(queue[right]) > 0) c = queue[child = right]; @@ -773,7 +883,7 @@ public class ScheduledThreadPoolExecutor int newCapacity = oldCapacity + (oldCapacity >> 1); // grow 50% if (newCapacity < 0) // overflow newCapacity = Integer.MAX_VALUE; - queue = Java6Arrays.copyOf(queue, newCapacity); + queue = Arrays.copyOf(queue, newCapacity); } /** @@ -816,7 +926,7 @@ public class ScheduledThreadPoolExecutor setIndex(queue[i], -1); int s = --size; - ScheduledFutureTask replacement = queue[s]; + RunnableScheduledFuture replacement = queue[s]; queue[s] = null; if (s != i) { siftDown(i, replacement); @@ -847,7 +957,7 @@ public class ScheduledThreadPoolExecutor return Integer.MAX_VALUE; } - public ScheduledFutureTask peek() { + public RunnableScheduledFuture peek() { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -860,7 +970,7 @@ public class ScheduledThreadPoolExecutor public boolean offer(Runnable x) { if (x == null) throw new NullPointerException(); - ScheduledFutureTask e = (ScheduledFutureTask)x; + RunnableScheduledFuture e = (RunnableScheduledFuture)x; final ReentrantLock lock = this.lock; lock.lock(); try { @@ -902,9 +1012,9 @@ public class ScheduledThreadPoolExecutor * holding lock. * @param f the task to remove and return */ - private ScheduledFutureTask finishPoll(ScheduledFutureTask f) { + private RunnableScheduledFuture finishPoll(RunnableScheduledFuture f) { int s = --size; - ScheduledFutureTask x = queue[s]; + RunnableScheduledFuture x = queue[s]; queue[s] = null; if (s != 0) siftDown(0, x); @@ -912,11 +1022,11 @@ public class ScheduledThreadPoolExecutor return f; } - public ScheduledFutureTask poll() { + public RunnableScheduledFuture poll() { final ReentrantLock lock = this.lock; lock.lock(); try { - ScheduledFutureTask first = queue[0]; + RunnableScheduledFuture first = queue[0]; if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0) return null; else @@ -926,12 +1036,12 @@ public class ScheduledThreadPoolExecutor } } - public ScheduledFutureTask take() throws InterruptedException { + public RunnableScheduledFuture take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { for (;;) { - ScheduledFutureTask first = queue[0]; + RunnableScheduledFuture first = queue[0]; if (first == null) available.await(); else { @@ -959,14 +1069,14 @@ public class ScheduledThreadPoolExecutor } } - public ScheduledFutureTask poll(long timeout, TimeUnit unit) + public RunnableScheduledFuture 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]; + RunnableScheduledFuture first = queue[0]; if (first == null) { if (nanos <= 0) return null; @@ -1005,7 +1115,7 @@ public class ScheduledThreadPoolExecutor lock.lock(); try { for (int i = 0; i < size; i++) { - ScheduledFutureTask t = queue[i]; + RunnableScheduledFuture t = queue[i]; if (t != null) { queue[i] = null; setIndex(t, -1); @@ -1021,8 +1131,8 @@ public class ScheduledThreadPoolExecutor * 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]; + private RunnableScheduledFuture pollExpired() { + RunnableScheduledFuture first = queue[0]; if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0) return null; return finishPoll(first); @@ -1036,7 +1146,7 @@ public class ScheduledThreadPoolExecutor final ReentrantLock lock = this.lock; lock.lock(); try { - ScheduledFutureTask first; + RunnableScheduledFuture first; int n = 0; while ((first = pollExpired()) != null) { c.add(first); @@ -1058,7 +1168,7 @@ public class ScheduledThreadPoolExecutor final ReentrantLock lock = this.lock; lock.lock(); try { - ScheduledFutureTask first; + RunnableScheduledFuture first; int n = 0; while (n < maxElements && (first = pollExpired()) != null) { c.add(first); @@ -1074,7 +1184,7 @@ public class ScheduledThreadPoolExecutor final ReentrantLock lock = this.lock; lock.lock(); try { - return Java6Arrays.copyOf(queue, size, Object[].class); + return Arrays.copyOf(queue, size, Object[].class); } finally { lock.unlock(); } @@ -1086,7 +1196,7 @@ public class ScheduledThreadPoolExecutor lock.lock(); try { if (a.length < size) - return (T[]) Java6Arrays.copyOf(queue, size, a.getClass()); + return (T[]) Arrays.copyOf(queue, size, a.getClass()); System.arraycopy(queue, 0, a, 0, size); if (a.length > size) a[size] = null; @@ -1097,18 +1207,18 @@ public class ScheduledThreadPoolExecutor } public Iterator<Runnable> iterator() { - return new Itr(Java6Arrays.copyOf(queue, size)); + return new Itr(Arrays.copyOf(queue, size)); } /** * Snapshot iterator that works off copy of underlying q array. */ private class Itr implements Iterator<Runnable> { - final ScheduledFutureTask[] array; + final RunnableScheduledFuture[] 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) { + Itr(RunnableScheduledFuture[] array) { this.array = array; } diff --git a/concurrent/src/main/java/java/util/concurrent/Semaphore.java b/concurrent/src/main/java/java/util/concurrent/Semaphore.java index 1052364..1f5c00e 100644 --- a/concurrent/src/main/java/java/util/concurrent/Semaphore.java +++ b/concurrent/src/main/java/java/util/concurrent/Semaphore.java @@ -212,10 +212,8 @@ public class Semaphore implements java.io.Serializable { } protected int tryAcquireShared(int acquires) { - Thread current = Thread.currentThread(); for (;;) { - Thread first = getFirstQueuedThread(); - if (first != null && first != current) + if (hasQueuedPredecessors()) return -1; int available = getState(); int remaining = available - acquires; diff --git a/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java b/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java index 0d297ff..5f9e8d7 100644 --- a/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java +++ b/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java @@ -419,9 +419,9 @@ public class SynchronousQueue<E> extends AbstractQueue<E> else if (s.waiter == null) s.waiter = w; // establish waiter so can park next iter else if (!timed) - LockSupport.park(); + LockSupport.park(this); else if (nanos > spinForTimeoutThreshold) - LockSupport.parkNanos(nanos); + LockSupport.parkNanos(this, nanos); } } @@ -711,9 +711,9 @@ public class SynchronousQueue<E> extends AbstractQueue<E> else if (s.waiter == null) s.waiter = w; else if (!timed) - LockSupport.park(); + LockSupport.park(this); else if (nanos > spinForTimeoutThreshold) - LockSupport.parkNanos(nanos); + LockSupport.parkNanos(this, nanos); } } @@ -991,19 +991,6 @@ public class SynchronousQueue<E> extends AbstractQueue<E> return null; } - - static class EmptyIterator<E> implements Iterator<E> { - public boolean hasNext() { - return false; - } - public E next() { - throw new NoSuchElementException(); - } - public void remove() { - throw new IllegalStateException(); - } - } - /** * Returns an empty iterator in which <tt>hasNext</tt> always returns * <tt>false</tt>. @@ -1011,7 +998,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E> * @return an empty iterator */ public Iterator<E> iterator() { - return new EmptyIterator<E>(); + return Collections.<E>emptySet().iterator(); // android-changed } /** diff --git a/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java b/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java index d0c934d..c362c99 100644 --- a/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java +++ b/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java @@ -92,9 +92,12 @@ import java.util.*; * 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> + * disables idle threads from ever terminating prior to shut down. By + * default, the keep-alive policy applies only when there are more + * than corePoolSizeThreads. But method {@link + * #allowCoreThreadTimeOut(boolean)} can be used to apply this + * time-out policy to core threads as well, so long as the + * keepAliveTime value is non-zero. </dd> * * <dt>Queuing</dt> * @@ -228,7 +231,8 @@ import java.util.*; * 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> + * keep-alive times, using a lower bound of zero core threads and/or + * setting {@link #allowCoreThreadTimeOut(boolean)}. </dd> * * </dl> * @@ -473,13 +477,22 @@ public class ThreadPoolExecutor extends AbstractExecutorService { /** * 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. + * present or if allowCoreThreadTimeOut. Otherwise they wait + * forever for new work. */ private volatile long keepAliveTime; /** + * If false (default), core threads stay alive even when idle. + * If true, core threads use keepAliveTime to time out waiting + * for work. + */ + private volatile boolean allowCoreThreadTimeOut; + + /** * Core pool size is the minimum number of workers to keep alive - * (and not allow to time out etc). + * (and not allow to time out etc) unless allowCoreThreadTimeOut + * is set, in which case the minimum is zero. */ private volatile int corePoolSize; @@ -941,7 +954,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService { int c = ctl.get(); if (runStateLessThan(c, STOP)) { if (!completedAbruptly) { - int min = corePoolSize; + int min = allowCoreThreadTimeOut ? 0 : corePoolSize; if (min == 0 && ! workQueue.isEmpty()) min = 1; if (workerCountOf(c) >= min) @@ -961,7 +974,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * 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}) + * {@code allowCoreThreadTimeOut || workerCount > corePoolSize}) * both before and after the timed wait. * * @return task, or null if the worker must exit, in which case @@ -985,7 +998,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService { for (;;) { int wc = workerCountOf(c); - timed = wc > corePoolSize; + timed = allowCoreThreadTimeOut || wc > corePoolSize; if (wc <= maximumPoolSize && ! (timedOut && timed)) break; @@ -1096,7 +1109,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * methods instead of this general purpose constructor. * * @param corePoolSize the number of threads to keep in the pool, even - * if they are idle + * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param maximumPoolSize the maximum number of threads to allow in the * pool * @param keepAliveTime when the number of threads is greater than @@ -1127,7 +1140,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * parameters and default rejected execution handler. * * @param corePoolSize the number of threads to keep in the pool, even - * if they are idle + * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param maximumPoolSize the maximum number of threads to allow in the * pool * @param keepAliveTime when the number of threads is greater than @@ -1162,7 +1175,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * parameters and default thread factory. * * @param corePoolSize the number of threads to keep in the pool, even - * if they are idle + * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param maximumPoolSize the maximum number of threads to allow in the * pool * @param keepAliveTime when the number of threads is greater than @@ -1197,7 +1210,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * parameters. * * @param corePoolSize the number of threads to keep in the pool, even - * if they are idle + * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param maximumPoolSize the maximum number of threads to allow in the * pool * @param keepAliveTime when the number of threads is greater than @@ -1518,6 +1531,50 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } /** + * Returns true if this pool allows core threads to time out and + * terminate if no tasks arrive within the keepAlive time, being + * replaced if needed when new tasks arrive. When true, the same + * keep-alive policy applying to non-core threads applies also to + * core threads. When false (the default), core threads are never + * terminated due to lack of incoming tasks. + * + * @return {@code true} if core threads are allowed to time out, + * else {@code false} + * + * @since 1.6 + */ + public boolean allowsCoreThreadTimeOut() { + return allowCoreThreadTimeOut; + } + + /** + * Sets the policy governing whether core threads may time out and + * terminate if no tasks arrive within the keep-alive time, being + * replaced if needed when new tasks arrive. When false, core + * threads are never terminated due to lack of incoming + * tasks. When true, the same keep-alive policy applying to + * non-core threads applies also to core threads. To avoid + * continual thread replacement, the keep-alive time must be + * greater than zero when setting {@code true}. This method + * should in general be called before the pool is actively used. + * + * @param value {@code true} if should time out, else {@code false} + * @throws IllegalArgumentException if value is {@code true} + * and the current keep-alive time is not greater than zero + * + * @since 1.6 + */ + public void allowCoreThreadTimeOut(boolean value) { + if (value && keepAliveTime <= 0) + throw new IllegalArgumentException("Core threads must have nonzero keep alive times"); + if (value != allowCoreThreadTimeOut) { + allowCoreThreadTimeOut = value; + if (value) + interruptIdleWorkers(); + } + } + + /** * Sets the maximum allowed 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 @@ -1564,6 +1621,8 @@ public class ThreadPoolExecutor extends AbstractExecutorService { public void setKeepAliveTime(long time, TimeUnit unit) { if (time < 0) throw new IllegalArgumentException(); + if (time == 0 && allowsCoreThreadTimeOut()) + throw new IllegalArgumentException("Core threads must have nonzero keep alive times"); long keepAliveTime = unit.toNanos(time); long delta = keepAliveTime - this.keepAliveTime; this.keepAliveTime = keepAliveTime; diff --git a/concurrent/src/main/java/java/util/concurrent/TimeUnit.java b/concurrent/src/main/java/java/util/concurrent/TimeUnit.java index 412d28a..ae78fda 100644 --- a/concurrent/src/main/java/java/util/concurrent/TimeUnit.java +++ b/concurrent/src/main/java/java/util/concurrent/TimeUnit.java @@ -40,7 +40,6 @@ package java.util.concurrent; * @author Doug Lea */ public enum TimeUnit { - /** TimeUnit which represents one nanosecond. */ NANOSECONDS { public long toNanos(long d) { return d; } public long toMicros(long d) { return d/(C1/C0); } @@ -52,7 +51,6 @@ public enum TimeUnit { 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 { public long toNanos(long d) { return x(d, C1/C0, MAX/(C1/C0)); } public long toMicros(long d) { return d; } @@ -64,7 +62,6 @@ public enum TimeUnit { 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 { 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)); } @@ -76,7 +73,6 @@ public enum TimeUnit { 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 { 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)); } @@ -87,6 +83,39 @@ public enum TimeUnit { 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; } + }, + MINUTES { + public long toNanos(long d) { return x(d, C4/C0, MAX/(C4/C0)); } + public long toMicros(long d) { return x(d, C4/C1, MAX/(C4/C1)); } + public long toMillis(long d) { return x(d, C4/C2, MAX/(C4/C2)); } + public long toSeconds(long d) { return x(d, C4/C3, MAX/(C4/C3)); } + public long toMinutes(long d) { return d; } + public long toHours(long d) { return d/(C5/C4); } + public long toDays(long d) { return d/(C6/C4); } + public long convert(long d, TimeUnit u) { return u.toMinutes(d); } + int excessNanos(long d, long m) { return 0; } + }, + HOURS { + public long toNanos(long d) { return x(d, C5/C0, MAX/(C5/C0)); } + public long toMicros(long d) { return x(d, C5/C1, MAX/(C5/C1)); } + public long toMillis(long d) { return x(d, C5/C2, MAX/(C5/C2)); } + public long toSeconds(long d) { return x(d, C5/C3, MAX/(C5/C3)); } + public long toMinutes(long d) { return x(d, C5/C4, MAX/(C5/C4)); } + public long toHours(long d) { return d; } + public long toDays(long d) { return d/(C6/C5); } + public long convert(long d, TimeUnit u) { return u.toHours(d); } + int excessNanos(long d, long m) { return 0; } + }, + DAYS { + public long toNanos(long d) { return x(d, C6/C0, MAX/(C6/C0)); } + public long toMicros(long d) { return x(d, C6/C1, MAX/(C6/C1)); } + public long toMillis(long d) { return x(d, C6/C2, MAX/(C6/C2)); } + public long toSeconds(long d) { return x(d, C6/C3, MAX/(C6/C3)); } + public long toMinutes(long d) { return x(d, C6/C4, MAX/(C6/C4)); } + public long toHours(long d) { return x(d, C6/C5, MAX/(C6/C5)); } + public long toDays(long d) { return d; } + public long convert(long d, TimeUnit u) { return u.toDays(d); } + int excessNanos(long d, long m) { return 0; } }; // Handy constants for conversion methods @@ -193,8 +222,9 @@ public enum TimeUnit { * or <tt>Long.MIN_VALUE</tt> if conversion would negatively * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow. * @see #convert + * @since 1.6 */ - long toMinutes(long duration) { + public long toMinutes(long duration) { throw new AbstractMethodError(); } @@ -205,8 +235,9 @@ public enum TimeUnit { * or <tt>Long.MIN_VALUE</tt> if conversion would negatively * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow. * @see #convert + * @since 1.6 */ - long toHours(long duration) { + public long toHours(long duration) { throw new AbstractMethodError(); } @@ -215,8 +246,9 @@ public enum TimeUnit { * @param duration the duration * @return the converted duration * @see #convert + * @since 1.6 */ - long toDays(long duration) { + public long toDays(long duration) { throw new AbstractMethodError(); } 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 ba60556..c774d21 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java @@ -21,10 +21,7 @@ import sun.misc.Unsafe; public class AtomicBoolean implements java.io.Serializable { private static final long serialVersionUID = 4654671469794556979L; // setup to use Unsafe.compareAndSwapInt for updates - // BEGIN android-changed - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; - // END android-changed - + private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed private static final long valueOffset; static { @@ -103,6 +100,17 @@ public class AtomicBoolean implements java.io.Serializable { } /** + * Eventually sets to the given value. + * + * @param newValue the new value + * @since 1.6 + */ + public final void lazySet(boolean newValue) { + int v = newValue ? 1 : 0; + unsafe.putOrderedInt(this, valueOffset, v); + } + + /** * Atomically sets to the given value and returns the previous value. * * @param newValue the new 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 1aa32f4..16dd568 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java @@ -24,9 +24,7 @@ public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates - // BEGIN android-changed - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; - // END android-changed + private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed private static final long valueOffset; static { @@ -72,6 +70,16 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** + * Eventually sets to the given value. + * + * @param newValue the new value + * @since 1.6 + */ + public final void lazySet(int newValue) { + unsafe.putOrderedInt(this, valueOffset, newValue); + } + + /** * Atomically sets to the given value and returns the old value. * * @param newValue the new 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 312c76d..b6d6cc5 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java @@ -19,33 +19,30 @@ import java.util.*; public class AtomicIntegerArray implements java.io.Serializable { private static final long serialVersionUID = 2862133569453604235L; - // setup to use Unsafe.compareAndSwapInt for updates - // BEGIN android-changed - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; - // END android-changed + private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed private static final int base = unsafe.arrayBaseOffset(int[].class); private static final int scale = unsafe.arrayIndexScale(int[].class); private final int[] array; - private long rawIndex(int i) { + private long checkedByteOffset(int i) { if (i < 0 || i >= array.length) throw new IndexOutOfBoundsException("index " + i); - // BEGIN android-changed - // avoid memory corruption + + return byteOffset(i); + } + + private static long byteOffset(int i) { return base + (long) i * scale; - // END android-changed } /** - * Creates a new AtomicIntegerArray of given length. + * Creates a new AtomicIntegerArray of the given length, with all + * elements initially zero. * * @param length the length of the array */ public AtomicIntegerArray(int length) { array = new int[length]; - // must perform at least one volatile write to conform to JMM - if (length > 0) - unsafe.putIntVolatile(array, rawIndex(0), 0); } /** @@ -56,17 +53,8 @@ public class AtomicIntegerArray implements java.io.Serializable { * @throws NullPointerException if array is null */ public AtomicIntegerArray(int[] array) { - if (array == null) - throw new NullPointerException(); - int length = array.length; - this.array = new int[length]; - if (length > 0) { - int last = length-1; - for (int i = 0; i < last; ++i) - this.array[i] = array[i]; - // Do the last write as volatile - unsafe.putIntVolatile(this.array, rawIndex(last), array[last]); - } + // Visibility guaranteed by final field guarantees + this.array = array.clone(); } /** @@ -85,7 +73,11 @@ public class AtomicIntegerArray implements java.io.Serializable { * @return the current value */ public final int get(int i) { - return unsafe.getIntVolatile(array, rawIndex(i)); + return getRaw(checkedByteOffset(i)); + } + + private int getRaw(long offset) { + return unsafe.getIntVolatile(array, offset); } /** @@ -95,7 +87,18 @@ public class AtomicIntegerArray implements java.io.Serializable { * @param newValue the new value */ public final void set(int i, int newValue) { - unsafe.putIntVolatile(array, rawIndex(i), newValue); + unsafe.putIntVolatile(array, checkedByteOffset(i), newValue); + } + + /** + * Eventually sets the element at position {@code i} to the given value. + * + * @param i the index + * @param newValue the new value + * @since 1.6 + */ + public final void lazySet(int i, int newValue) { + unsafe.putOrderedInt(array, checkedByteOffset(i), newValue); } /** @@ -107,9 +110,10 @@ public class AtomicIntegerArray implements java.io.Serializable { * @return the previous value */ public final int getAndSet(int i, int newValue) { + long offset = checkedByteOffset(i); while (true) { - int current = get(i); - if (compareAndSet(i, current, newValue)) + int current = getRaw(offset); + if (compareAndSetRaw(offset, current, newValue)) return current; } } @@ -125,8 +129,11 @@ 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), - expect, update); + return compareAndSetRaw(checkedByteOffset(i), expect, update); + } + + private boolean compareAndSetRaw(long offset, int expect, int update) { + return unsafe.compareAndSwapInt(array, offset, expect, update); } /** @@ -153,12 +160,7 @@ public class AtomicIntegerArray implements java.io.Serializable { * @return the previous value */ public final int getAndIncrement(int i) { - while (true) { - int current = get(i); - int next = current + 1; - if (compareAndSet(i, current, next)) - return current; - } + return getAndAdd(i, 1); } /** @@ -168,12 +170,7 @@ public class AtomicIntegerArray implements java.io.Serializable { * @return the previous value */ public final int getAndDecrement(int i) { - while (true) { - int current = get(i); - int next = current - 1; - if (compareAndSet(i, current, next)) - return current; - } + return getAndAdd(i, -1); } /** @@ -184,10 +181,10 @@ public class AtomicIntegerArray implements java.io.Serializable { * @return the previous value */ public final int getAndAdd(int i, int delta) { + long offset = checkedByteOffset(i); while (true) { - int current = get(i); - int next = current + delta; - if (compareAndSet(i, current, next)) + int current = getRaw(offset); + if (compareAndSetRaw(offset, current, current + delta)) return current; } } @@ -199,12 +196,7 @@ public class AtomicIntegerArray implements java.io.Serializable { * @return the updated value */ public final int incrementAndGet(int i) { - while (true) { - int current = get(i); - int next = current + 1; - if (compareAndSet(i, current, next)) - return next; - } + return addAndGet(i, 1); } /** @@ -214,12 +206,7 @@ public class AtomicIntegerArray implements java.io.Serializable { * @return the updated value */ public final int decrementAndGet(int i) { - while (true) { - int current = get(i); - int next = current - 1; - if (compareAndSet(i, current, next)) - return next; - } + return addAndGet(i, -1); } /** @@ -230,22 +217,32 @@ public class AtomicIntegerArray implements java.io.Serializable { * @return the updated value */ public final int addAndGet(int i, int delta) { + long offset = checkedByteOffset(i); while (true) { - int current = get(i); + int current = getRaw(offset); int next = current + delta; - if (compareAndSet(i, current, next)) + if (compareAndSetRaw(offset, current, next)) return next; } } /** * Returns the String representation of the current values of array. - * @return the String representation of the current values of array. + * @return the String representation of the current values of array */ public String toString() { - if (array.length > 0) // force volatile read - get(0); - return Arrays.toString(array); + int iMax = array.length - 1; + if (iMax == -1) + return "[]"; + + StringBuilder b = new StringBuilder(); + b.append('['); + for (int i = 0; ; i++) { + b.append(getRaw(byteOffset(i))); + if (i == iMax) + return b.append(']').toString(); + b.append(", "); + } } } 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 f8d2c81..476bb0a 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java @@ -5,12 +5,10 @@ */ package java.util.concurrent.atomic; +import dalvik.system.VMStack; 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 {@code volatile int} fields of designated classes. @@ -100,6 +98,17 @@ public abstract class AtomicIntegerFieldUpdater<T> { public abstract void set(T obj, int newValue); /** + * Eventually sets the field of the given object managed by this + * updater to the given updated value. + * + * @param obj An object whose field to set + * @param newValue the new value + * @since 1.6 + */ + public abstract void lazySet(T obj, int newValue); + + + /** * Gets the current value held in the field of the given object managed * by this updater. * @@ -226,9 +235,7 @@ public abstract class AtomicIntegerFieldUpdater<T> { * Standard hotspot implementation using intrinsics */ private static class AtomicIntegerFieldUpdaterImpl<T> extends AtomicIntegerFieldUpdater<T> { - // BEGIN android-changed - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; - // END android-changed + private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed private final long offset; private final Class<T> tclass; private final Class cclass; @@ -244,6 +251,7 @@ public abstract class AtomicIntegerFieldUpdater<T> { // END android-changed modifiers = field.getModifiers(); + // BEGIN android-added SecurityManager smgr = System.getSecurityManager(); if (smgr != null) { int type = Modifier.isPublic(modifiers) @@ -251,6 +259,13 @@ public abstract class AtomicIntegerFieldUpdater<T> { smgr.checkMemberAccess(tclass, type); smgr.checkPackageAccess(tclass.getPackage().getName()); } + // END android-added + // BEGIN android-removed + // modifiers = field.getModifiers(); + // sun.reflect.misc.ReflectUtil.ensureMemberAccess( + // caller, tclass, null, modifiers); + // sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass); + // END android-removed } catch (Exception ex) { throw new RuntimeException(ex); } @@ -290,6 +305,11 @@ public abstract class AtomicIntegerFieldUpdater<T> { unsafe.putIntVolatile(obj, offset, newValue); } + public void lazySet(T obj, int newValue) { + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); + unsafe.putOrderedInt(obj, offset, newValue); + } + public final int get(T obj) { if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); return unsafe.getIntVolatile(obj, offset); 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 9b56002..e1f5dc7 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java @@ -24,9 +24,7 @@ public class AtomicLong extends Number implements java.io.Serializable { private static final long serialVersionUID = 1927816293512124184L; // setup to use Unsafe.compareAndSwapLong for updates - // BEGIN android-changed - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; - // END android-changed + private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed private static final long valueOffset; /** @@ -97,6 +95,16 @@ public class AtomicLong extends Number implements java.io.Serializable { } /** + * Eventually sets to the given value. + * + * @param newValue the new value + * @since 1.6 + */ + public final void lazySet(long newValue) { + unsafe.putOrderedLong(this, valueOffset, newValue); + } + + /** * Atomically sets to the given value and returns the old value. * * @param newValue the new value 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 187acbc..33e6914 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java @@ -18,33 +18,30 @@ import java.util.*; public class AtomicLongArray implements java.io.Serializable { private static final long serialVersionUID = -2308431214976778248L; - // setup to use Unsafe.compareAndSwapInt for updates - // BEGIN android-changed - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; - // END android-changed + private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed private static final int base = unsafe.arrayBaseOffset(long[].class); private static final int scale = unsafe.arrayIndexScale(long[].class); private final long[] array; - private long rawIndex(int i) { + private long checkedByteOffset(int i) { if (i < 0 || i >= array.length) throw new IndexOutOfBoundsException("index " + i); - // BEGIN android-changed - // avoid memory corruption + + return byteOffset(i); + } + + private static long byteOffset(int i) { return base + (long) i * scale; - // END android-changed } /** - * Creates a new AtomicLongArray of given length. + * Creates a new AtomicLongArray of the given length, with all + * elements initially zero. * * @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) - unsafe.putLongVolatile(array, rawIndex(0), 0); } /** @@ -55,17 +52,8 @@ public class AtomicLongArray implements java.io.Serializable { * @throws NullPointerException if array is null */ public AtomicLongArray(long[] array) { - if (array == null) - throw new NullPointerException(); - int length = array.length; - this.array = new long[length]; - if (length > 0) { - int last = length-1; - for (int i = 0; i < last; ++i) - this.array[i] = array[i]; - // Do the last write as volatile - unsafe.putLongVolatile(this.array, rawIndex(last), array[last]); - } + // Visibility guaranteed by final field guarantees + this.array = array.clone(); } /** @@ -84,7 +72,11 @@ public class AtomicLongArray implements java.io.Serializable { * @return the current value */ public final long get(int i) { - return unsafe.getLongVolatile(array, rawIndex(i)); + return getRaw(checkedByteOffset(i)); + } + + private long getRaw(long offset) { + return unsafe.getLongVolatile(array, offset); } /** @@ -94,10 +86,22 @@ public class AtomicLongArray implements java.io.Serializable { * @param newValue the new value */ public final void set(int i, long newValue) { - unsafe.putLongVolatile(array, rawIndex(i), newValue); + unsafe.putLongVolatile(array, checkedByteOffset(i), newValue); } /** + * Eventually sets the element at position {@code i} to the given value. + * + * @param i the index + * @param newValue the new value + * @since 1.6 + */ + public final void lazySet(int i, long newValue) { + unsafe.putOrderedLong(array, checkedByteOffset(i), newValue); + } + + + /** * Atomically sets the element at position {@code i} to the given value * and returns the old value. * @@ -106,16 +110,17 @@ public class AtomicLongArray implements java.io.Serializable { * @return the previous value */ public final long getAndSet(int i, long newValue) { + long offset = checkedByteOffset(i); while (true) { - long current = get(i); - if (compareAndSet(i, current, newValue)) + long current = getRaw(offset); + if (compareAndSetRaw(offset, current, newValue)) return current; } } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} 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 @@ -124,13 +129,16 @@ 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), - expect, update); + return compareAndSetRaw(checkedByteOffset(i), expect, update); + } + + private boolean compareAndSetRaw(long offset, long expect, long update) { + return unsafe.compareAndSwapLong(array, offset, expect, update); } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. + * 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 @@ -152,12 +160,7 @@ public class AtomicLongArray implements java.io.Serializable { * @return the previous value */ public final long getAndIncrement(int i) { - while (true) { - long current = get(i); - long next = current + 1; - if (compareAndSet(i, current, next)) - return current; - } + return getAndAdd(i, 1); } /** @@ -167,12 +170,7 @@ public class AtomicLongArray implements java.io.Serializable { * @return the previous value */ public final long getAndDecrement(int i) { - while (true) { - long current = get(i); - long next = current - 1; - if (compareAndSet(i, current, next)) - return current; - } + return getAndAdd(i, -1); } /** @@ -183,10 +181,10 @@ public class AtomicLongArray implements java.io.Serializable { * @return the previous value */ public final long getAndAdd(int i, long delta) { + long offset = checkedByteOffset(i); while (true) { - long current = get(i); - long next = current + delta; - if (compareAndSet(i, current, next)) + long current = getRaw(offset); + if (compareAndSetRaw(offset, current, current + delta)) return current; } } @@ -198,12 +196,7 @@ public class AtomicLongArray implements java.io.Serializable { * @return the updated value */ public final long incrementAndGet(int i) { - while (true) { - long current = get(i); - long next = current + 1; - if (compareAndSet(i, current, next)) - return next; - } + return addAndGet(i, 1); } /** @@ -213,12 +206,7 @@ public class AtomicLongArray implements java.io.Serializable { * @return the updated value */ public final long decrementAndGet(int i) { - while (true) { - long current = get(i); - long next = current - 1; - if (compareAndSet(i, current, next)) - return next; - } + return addAndGet(i, -1); } /** @@ -229,22 +217,32 @@ public class AtomicLongArray implements java.io.Serializable { * @return the updated value */ public long addAndGet(int i, long delta) { + long offset = checkedByteOffset(i); while (true) { - long current = get(i); + long current = getRaw(offset); long next = current + delta; - if (compareAndSet(i, current, next)) + if (compareAndSetRaw(offset, current, next)) return next; } } /** * Returns the String representation of the current values of array. - * @return the String representation of the current values of array. + * @return the String representation of the current values of array */ public String toString() { - if (array.length > 0) // force volatile read - get(0); - return Arrays.toString(array); + int iMax = array.length - 1; + if (iMax == -1) + return "[]"; + + StringBuilder b = new StringBuilder(); + b.append('['); + for (int i = 0; ; i++) { + b.append(getRaw(byteOffset(i))); + if (i == iMax) + return b.append(']').toString(); + b.append(", "); + } } } 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 d97c2e4..427a9e1 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java @@ -5,12 +5,10 @@ */ package java.util.concurrent.atomic; +import dalvik.system.VMStack; 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 {@code volatile long} fields of designated classes. @@ -103,6 +101,16 @@ public abstract class AtomicLongFieldUpdater<T> { public abstract void set(T obj, long newValue); /** + * Eventually sets the field of the given object managed by this + * updater to the given updated value. + * + * @param obj An object whose field to set + * @param newValue the new value + * @since 1.6 + */ + public abstract void lazySet(T obj, long newValue); + + /** * Gets the current value held in the field of the given object managed * by this updater. * @@ -226,9 +234,7 @@ public abstract class AtomicLongFieldUpdater<T> { } private static class CASUpdater<T> extends AtomicLongFieldUpdater<T> { - // BEGIN android-changed - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; - // END android-changed + private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed private final long offset; private final Class<T> tclass; private final Class cclass; @@ -239,10 +245,9 @@ public abstract class AtomicLongFieldUpdater<T> { int modifiers = 0; try { field = tclass.getDeclaredField(fieldName); - // BEGIN android-changed - caller = VMStack.getStackClass2(); - // END android-changed + caller = VMStack.getStackClass2(); // android-changed modifiers = field.getModifiers(); + // BEGIN android-added SecurityManager smgr = System.getSecurityManager(); if (smgr != null) { int type = Modifier.isPublic(modifiers) @@ -250,6 +255,12 @@ public abstract class AtomicLongFieldUpdater<T> { smgr.checkMemberAccess(tclass, type); smgr.checkPackageAccess(tclass.getPackage().getName()); } + // END android-added + // BEGIN android-removed + // sun.reflect.misc.ReflectUtil.ensureMemberAccess( + // caller, tclass, null, modifiers); + // sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass); + // END android-removed } catch (Exception ex) { throw new RuntimeException(ex); } @@ -289,6 +300,11 @@ public abstract class AtomicLongFieldUpdater<T> { unsafe.putLongVolatile(obj, offset, newValue); } + public void lazySet(T obj, long newValue) { + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); + unsafe.putOrderedLong(obj, offset, newValue); + } + public long get(T obj) { if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); return unsafe.getLongVolatile(obj, offset); @@ -312,9 +328,7 @@ public abstract class AtomicLongFieldUpdater<T> { private static class LockedUpdater<T> extends AtomicLongFieldUpdater<T> { - // BEGIN android-changed - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; - // END android-changed + private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed private final long offset; private final Class<T> tclass; private final Class cclass; @@ -325,10 +339,9 @@ public abstract class AtomicLongFieldUpdater<T> { int modifiers = 0; try { field = tclass.getDeclaredField(fieldName); - // BEGIN android-changed - caller = VMStack.getStackClass2(); - // END android-changed + caller = VMStack.getStackClass2(); // android-changed modifiers = field.getModifiers(); + // BEGIN android-added SecurityManager smgr = System.getSecurityManager(); if (smgr != null) { int type = Modifier.isPublic(modifiers) @@ -336,6 +349,12 @@ public abstract class AtomicLongFieldUpdater<T> { smgr.checkMemberAccess(tclass, type); smgr.checkPackageAccess(tclass.getPackage().getName()); } + // END android-added + // BEGIN android-removed + // sun.reflect.misc.ReflectUtil.ensureMemberAccess( + // caller, tclass, null, modifiers); + // sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass); + // END android-removed } catch (Exception ex) { throw new RuntimeException(ex); } 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 1de9b1c..f041bbd 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java @@ -18,9 +18,7 @@ import sun.misc.Unsafe; public class AtomicReference<V> implements java.io.Serializable { private static final long serialVersionUID = -1848883965231344442L; - // BEGIN android-changed - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; - // END android-changed + private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed private static final long valueOffset; static { @@ -66,6 +64,16 @@ public class AtomicReference<V> implements java.io.Serializable { } /** + * Eventually sets to the given value. + * + * @param newValue the new value + * @since 1.6 + */ + public final void lazySet(V newValue) { + unsafe.putOrderedObject(this, valueOffset, newValue); + } + + /** * Atomically sets the value to the given updated value * if the current value {@code ==} the expected value. * @param expect the expected 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 fff6a1e..2501c2b 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java @@ -20,31 +20,30 @@ import java.util.*; public class AtomicReferenceArray<E> implements java.io.Serializable { private static final long serialVersionUID = -6209656149925076980L; - // BEGIN android-changed - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; - // END android-changed + private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed private static final int base = unsafe.arrayBaseOffset(Object[].class); private static final int scale = unsafe.arrayIndexScale(Object[].class); private final Object[] array; - private long rawIndex(int i) { + private long checkedByteOffset(int i) { if (i < 0 || i >= array.length) throw new IndexOutOfBoundsException("index " + i); - // BEGIN android-changed - // avoid memory corruption + + return byteOffset(i); + } + + private static long byteOffset(int i) { return base + (long) i * scale; - // END android-changed } /** - * Creates a new AtomicReferenceArray of given length. + * Creates a new AtomicReferenceArray of the given length, with all + * elements initially zero. + * * @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) - unsafe.putObjectVolatile(array, rawIndex(0), null); } /** @@ -55,18 +54,8 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { * @throws NullPointerException if array is null */ public AtomicReferenceArray(E[] array) { - if (array == null) - throw new NullPointerException(); - int length = array.length; - this.array = new Object[length]; - if (length > 0) { - int last = length-1; - for (int i = 0; i < last; ++i) - this.array[i] = array[i]; - // Do the last write as volatile - E e = array[last]; - unsafe.putObjectVolatile(this.array, rawIndex(last), e); - } + // Visibility guaranteed by final field guarantees + this.array = array.clone(); } /** @@ -85,7 +74,11 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { * @return the current value */ public final E get(int i) { - return (E) unsafe.getObjectVolatile(array, rawIndex(i)); + return getRaw(checkedByteOffset(i)); + } + + private E getRaw(long offset) { + return (E) unsafe.getObjectVolatile(array, offset); } /** @@ -95,9 +88,21 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { * @param newValue the new value */ public final void set(int i, E newValue) { - unsafe.putObjectVolatile(array, rawIndex(i), newValue); + unsafe.putObjectVolatile(array, checkedByteOffset(i), newValue); + } + + /** + * Eventually sets the element at position {@code i} to the given value. + * + * @param i the index + * @param newValue the new value + * @since 1.6 + */ + public final void lazySet(int i, E newValue) { + unsafe.putOrderedObject(array, checkedByteOffset(i), newValue); } + /** * Atomically sets the element at position {@code i} to the given * value and returns the old value. @@ -107,9 +112,10 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { * @return the previous value */ public final E getAndSet(int i, E newValue) { + long offset = checkedByteOffset(i); while (true) { - E current = get(i); - if (compareAndSet(i, current, newValue)) + E current = (E) getRaw(offset); + if (compareAndSetRaw(offset, current, newValue)) return current; } } @@ -117,6 +123,7 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { /** * 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 @@ -124,8 +131,11 @@ 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), - expect, update); + return compareAndSetRaw(checkedByteOffset(i), expect, update); + } + + private boolean compareAndSetRaw(long offset, E expect, E update) { + return unsafe.compareAndSwapObject(array, offset, expect, update); } /** @@ -147,12 +157,21 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { /** * Returns the String representation of the current values of array. - * @return the String representation of the current values of array. + * @return the String representation of the current values of array */ public String toString() { - if (array.length > 0) // force volatile read - get(0); - return Arrays.toString(array); + int iMax = array.length - 1; + if (iMax == -1) + return "[]"; + + StringBuilder b = new StringBuilder(); + b.append('['); + for (int i = 0; ; i++) { + b.append(getRaw(byteOffset(i))); + if (i == iMax) + return b.append(']').toString(); + b.append(", "); + } } } 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 fae134f..e53f2e9 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java @@ -5,12 +5,10 @@ */ package java.util.concurrent.atomic; +import dalvik.system.VMStack; 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 {@code volatile} reference fields of designated @@ -118,6 +116,16 @@ public abstract class AtomicReferenceFieldUpdater<T, V> { public abstract void set(T obj, V newValue); /** + * Eventually sets the field of the given object managed by this + * updater to the given updated value. + * + * @param obj An object whose field to set + * @param newValue the new value + * @since 1.6 + */ + public abstract void lazySet(T obj, V newValue); + + /** * Gets the current value held in the field of the given object managed * by this updater. * @@ -144,9 +152,7 @@ public abstract class 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 static final Unsafe unsafe = Unsafe.getUnsafe(); private final long offset; private final Class<T> tclass; private final Class<V> vclass; @@ -173,10 +179,9 @@ public abstract class AtomicReferenceFieldUpdater<T, V> { int modifiers = 0; try { field = tclass.getDeclaredField(fieldName); - // BEGIN android-changed - caller = VMStack.getStackClass2(); - // END android-changed + caller = VMStack.getStackClass2(); // android-changed modifiers = field.getModifiers(); + // BEGIN android-added SecurityManager smgr = System.getSecurityManager(); if (smgr != null) { int type = Modifier.isPublic(modifiers) @@ -184,6 +189,12 @@ public abstract class AtomicReferenceFieldUpdater<T, V> { smgr.checkMemberAccess(tclass, type); smgr.checkPackageAccess(tclass.getPackage().getName()); } + // END android-added + // BEGIN android-removed + // sun.reflect.misc.ReflectUtil.ensureMemberAccess( + // caller, tclass, null, modifiers); + // sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass); + // END android-removed fieldClass = field.getType(); } catch (Exception ex) { throw new RuntimeException(ex); @@ -245,6 +256,14 @@ public abstract class AtomicReferenceFieldUpdater<T, V> { unsafe.putObjectVolatile(obj, offset, newValue); } + public void lazySet(T obj, V newValue) { + if (obj == null || obj.getClass() != tclass || cclass != null || + (newValue != null && vclass != null && + vclass != newValue.getClass())) + updateCheck(obj, newValue); + unsafe.putOrderedObject(obj, offset, newValue); + } + public V get(T obj) { if (obj == null || obj.getClass() != tclass || cclass != null) targetCheck(obj); 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 index 2252e5c..4a4375d 100644 --- a/concurrent/src/main/java/java/util/concurrent/atomic/package-info.java +++ b/concurrent/src/main/java/java/util/concurrent/atomic/package-info.java @@ -63,6 +63,14 @@ * <li> {@code set} has the memory effects of writing (assigning) a * {@code volatile} variable. * + * <li> {@code lazySet} has the memory effects of writing (assigning) + * a {@code volatile} variable except that it permits reorderings with + * subsequent (but not previous) memory actions that do not themselves + * impose reordering constraints with ordinary non-{@code volatile} + * writes. Among other usage contexts, {@code lazySet} may apply when + * nulling out, for the sake of garbage collection, a reference that is + * never accessed again. + * * <li>{@code weakCompareAndSet} atomically reads and conditionally * writes a variable but does <em>not</em> * create any happens-before orderings, so provides no guarantees diff --git a/concurrent/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java b/concurrent/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java index bf2fe16..f3780e5 100644 --- a/concurrent/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java +++ b/concurrent/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java @@ -15,10 +15,11 @@ package java.util.concurrent.locks; * appropriately maintained values to help control and monitor access * and provide diagnostics. * + * @since 1.6 * @author Doug Lea */ -abstract class AbstractOwnableSynchronizer - implements java.io.Serializable { +public abstract class AbstractOwnableSynchronizer + implements java.io.Serializable { /** Use serial ID even though all fields transient. */ private static final long serialVersionUID = 3737899427754241961L; @@ -53,4 +54,4 @@ abstract class AbstractOwnableSynchronizer protected final Thread getExclusiveOwnerThread() { return exclusiveOwnerThread; } -}
\ No newline at end of file +} diff --git a/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java new file mode 100644 index 0000000..d805259 --- /dev/null +++ b/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java @@ -0,0 +1,2073 @@ +/* + * 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; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import sun.misc.Unsafe; + +/** + * A version of {@link AbstractQueuedSynchronizer} in + * which synchronization state is maintained as a <tt>long</tt>. + * This class has exactly the same structure, properties, and methods + * as <tt>AbstractQueuedSynchronizer</tt> with the exception + * that all state-related parameters and results are defined + * as <tt>long</tt> rather than <tt>int</tt>. This class + * may be useful when creating synchronizers such as + * multilevel locks and barriers that require + * 64 bits of state. + * + * <p>See {@link AbstractQueuedSynchronizer} for usage + * notes and examples. + * + * @since 1.6 + * @author Doug Lea + */ +public abstract class AbstractQueuedLongSynchronizer + extends AbstractOwnableSynchronizer + implements java.io.Serializable { + + private static final long serialVersionUID = 7373984972572414692L; + + /* + To keep sources in sync, the remainder of this source file is + exactly cloned from AbstractQueuedSynchronizer, replacing class + name and changing ints related with sync state to longs. Please + keep it that way. + */ + + /** + * Creates a new <tt>AbstractQueuedLongSynchronizer</tt> instance + * with initial synchronization state of zero. + */ + protected AbstractQueuedLongSynchronizer() { } + + /** + * Wait queue node class. + * + * <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 + * information about a thread in the predecessor of its node. A + * "status" field in each node keeps track of whether a thread + * should block. A node is signalled when its predecessor + * releases. Each node of the queue otherwise serves as a + * specific-notification-style monitor holding a single waiting + * thread. The status field does NOT control whether threads are + * granted locks etc though. A thread may try to acquire if it is + * first in the queue. But being first does not guarantee success; + * it only gives the right to contend. So the currently released + * 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. + * <pre> + * +------+ prev +-----+ +-----+ + * head | | <---- | | <---- | | tail + * +------+ +-----+ +-----+ + * </pre> + * + * <p>Insertion into a CLH queue requires only a single atomic + * operation on "tail", so there is a simple atomic point of + * demarcation from unqueued to queued. Similarly, dequeing + * involves only updating the "head". However, it takes a bit + * more work for nodes to determine who their successors are, + * in part to deal with possible cancellation due to timeouts + * and interrupts. + * + * <p>The "prev" links (not used in original CLH locks), are mainly + * needed to handle cancellation. If a node is cancelled, its + * successor is (normally) relinked to a non-cancelled + * 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 + * next link to determine which thread it is. Determination of + * successor must avoid races with newly queued nodes to set + * the "next" fields of their predecessors. This is solved + * when necessary by checking backwards from the atomically + * updated "tail" when a node's successor appears to be null. + * (Or, said differently, the next-links are an optimization + * so that we don't usually need a backward scan.) + * + * <p>Cancellation introduces some conservatism to the basic + * algorithms. Since we must poll for cancellation of other + * 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, 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 + * effort if there is never contention. Instead, the node + * is constructed and head and tail pointers are set upon first + * contention. + * + * <p>Threads waiting on Conditions use the same nodes, but + * use an additional link. Conditions only need to link nodes + * in simple (non-concurrent) linked queues because they are + * only accessed when exclusively held. Upon await, a node is + * inserted into a condition queue. Upon signal, the node is + * transferred to the main queue. A special value of status + * field is used to mark which queue a node is on. + * + * <p>Thanks go to Dave Dice, Mark Moir, Victor Luchangco, Bill + * Scherer and Michael Scott, along with members of JSR-166 + * expert group, for helpful ideas, discussions, and critiques + * 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 successor's thread needs unparking */ + static final int SIGNAL = -1; + /** waitStatus value to indicate thread is waiting on condition */ + static final int CONDITION = -2; + /** + * 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 + * cancels. To avoid races, acquire methods must + * first indicate they need a signal, + * then retry the atomic acquire, and then, + * on failure, block. + * 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: 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. + * Non-negative values mean that a node doesn't need to + * signal. So, most code doesn't need to check for particular + * values, just for sign. + * + * The field is initialized to 0 for normal sync nodes, and + * CONDITION for condition nodes. It is modified using CAS + * (or when possible, unconditional volatile writes). + */ + volatile int waitStatus; + + /** + * Link to predecessor node that current node/thread relies on + * for checking waitStatus. Assigned during enqueing, and nulled + * out (for sake of GC) only upon dequeuing. Also, upon + * cancellation of a predecessor, we short-circuit while + * finding a non-cancelled one, which will always exist + * because the head node is never cancelled: A node becomes + * head only as a result of successful acquire. A + * cancelled thread never succeeds in acquiring, and a thread only + * cancels itself, not any other node. + */ + volatile Node prev; + + /** + * Link to the successor node that the current node/thread + * 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. + */ + 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 + * linked queue to hold nodes while they are waiting on + * conditions. They are then transferred to the queue to + * re-acquire. And because conditions can only be exclusive, + * we save a field by using special value to indicate shared + * mode. + */ + Node nextWaiter; + + /** + * Returns true if node is waiting in shared mode + */ + final boolean isShared() { + return nextWaiter == SHARED; + } + + /** + * 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(); + else + return p; + } + + Node() { // Used to establish initial head or SHARED marker + } + + Node(Thread thread, Node mode) { // Used by addWaiter + this.nextWaiter = mode; + this.thread = thread; + } + + Node(Thread thread, int waitStatus) { // Used by Condition + this.waitStatus = waitStatus; + 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 + * CANCELLED. + */ + 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; + + /** + * The synchronization state. + */ + private volatile long state; + + /** + * Returns the current value of synchronization state. + * This operation has memory semantics of a <tt>volatile</tt> read. + * @return current state value + */ + protected final long getState() { + return state; + } + + /** + * Sets the value of synchronization state. + * This operation has memory semantics of a <tt>volatile</tt> write. + * @param newState the new state value + */ + protected final void setState(long newState) { + state = newState; + } + + /** + * Atomically sets synchronization state to the given updated + * 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. + */ + protected final boolean compareAndSetState(long expect, long update) { + // See below for intrinsics setup to support this + return unsafe.compareAndSwapLong(this, stateOffset, expect, update); + } + + // Queuing utilities + + /** + * 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 + */ + private Node enq(final Node node) { + for (;;) { + Node t = tail; + if (t == null) { // Must initialize + if (compareAndSetHead(new Node())) + tail = head; + } else { + node.prev = t; + if (compareAndSetTail(t, node)) { + t.next = node; + return t; + } + } + } + } + + /** + * Creates and enqueues node for current thread and given mode. + * + * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared + * @return the new node + */ + private Node addWaiter(Node mode) { + Node node = new Node(Thread.currentThread(), mode); + // Try the fast path of enq; backup to full enq on failure + Node pred = tail; + if (pred != null) { + node.prev = pred; + if (compareAndSetTail(pred, node)) { + pred.next = node; + return node; + } + } + enq(node); + return node; + } + + /** + * 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 + */ + private void setHead(Node node) { + head = node; + node.thread = null; + node.prev = null; + } + + /** + * Wakes up node's successor, if one exists. + * + * @param node the node + */ + private void unparkSuccessor(Node node) { + /* + * 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. + */ + 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. + */ + Node s = node.next; + 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; + } + if (s != null) + LockSupport.unpark(s.thread); + } + + /** + * 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, long propagate) { + Node h = head; // Record old head for check below + setHead(node); + /* + * 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()) + doReleaseShared(); + } + } + + // Utilities for various versions of acquire + + /** + * Cancels an ongoing attempt to acquire. + * + * @param node the node + */ + private void cancelAcquire(Node 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 + } + } + + /** + * 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 {@code true} if thread should block + */ + private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { + 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. + */ + return true; + if (ws > 0) { + /* + * Predecessor was cancelled. Skip over predecessors and + * indicate retry. + */ + do { + node.prev = pred = pred.prev; + } while (pred.waitStatus > 0); + pred.next = node; + } else { + /* + * 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, ws, Node.SIGNAL); + } + return false; + } + + /** + * Convenience method to interrupt current thread. + */ + private static void selfInterrupt() { + Thread.currentThread().interrupt(); + } + + /** + * Convenience method to park and then check if interrupted + * + * @return {@code true} if interrupted + */ + private final boolean parkAndCheckInterrupt() { + LockSupport.park(this); + return Thread.interrupted(); + } + + /* + * Various flavors of acquire, varying in exclusive/shared and + * control modes. Each is mostly the same, but annoyingly + * 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. + */ + + /** + * 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 {@code true} if interrupted while waiting + */ + final boolean acquireQueued(final Node node, long arg) { + boolean failed = true; + try { + boolean interrupted = false; + for (;;) { + final Node p = node.predecessor(); + if (p == head && tryAcquire(arg)) { + setHead(node); + p.next = null; // help GC + failed = false; + return interrupted; + } + if (shouldParkAfterFailedAcquire(p, node) && + parkAndCheckInterrupt()) + interrupted = true; + } + } finally { + if (failed) + cancelAcquire(node); + } + } + + /** + * Acquires in exclusive interruptible mode. + * @param arg the acquire argument + */ + private void doAcquireInterruptibly(long 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()) + throw new InterruptedException(); + } + } finally { + if (failed) + cancelAcquire(node); + } + } + + /** + * Acquires in exclusive timed mode. + * + * @param arg the acquire argument + * @param nanosTimeout max wait time + * @return {@code true} if acquired + */ + private boolean doAcquireNanos(long 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) + return false; + if (shouldParkAfterFailedAcquire(p, node) && + nanosTimeout > spinForTimeoutThreshold) + LockSupport.parkNanos(this, nanosTimeout); + long now = System.nanoTime(); + nanosTimeout -= now - lastTime; + lastTime = now; + if (Thread.interrupted()) + throw new InterruptedException(); + } + } finally { + if (failed) + cancelAcquire(node); + } + } + + /** + * Acquires in shared uninterruptible mode. + * @param arg the acquire argument + */ + private void doAcquireShared(long arg) { + final Node node = addWaiter(Node.SHARED); + boolean failed = true; + try { + boolean interrupted = false; + for (;;) { + final Node p = node.predecessor(); + if (p == head) { + long r = tryAcquireShared(arg); + if (r >= 0) { + setHeadAndPropagate(node, r); + p.next = null; // help GC + if (interrupted) + selfInterrupt(); + failed = false; + return; + } + } + if (shouldParkAfterFailedAcquire(p, node) && + parkAndCheckInterrupt()) + interrupted = true; + } + } finally { + if (failed) + cancelAcquire(node); + } + } + + /** + * Acquires in shared interruptible mode. + * @param arg the acquire argument + */ + private void doAcquireSharedInterruptibly(long arg) + throws InterruptedException { + final Node node = addWaiter(Node.SHARED); + boolean failed = true; + try { + for (;;) { + final Node p = node.predecessor(); + if (p == head) { + long r = tryAcquireShared(arg); + if (r >= 0) { + setHeadAndPropagate(node, r); + p.next = null; // help GC + failed = false; + return; + } + } + if (shouldParkAfterFailedAcquire(p, node) && + parkAndCheckInterrupt()) + throw new InterruptedException(); + } + } finally { + if (failed) + cancelAcquire(node); + } + } + + /** + * Acquires in shared timed mode. + * + * @param arg the acquire argument + * @param nanosTimeout max wait time + * @return {@code true} if acquired + */ + private boolean doAcquireSharedNanos(long 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(); + if (p == head) { + long r = tryAcquireShared(arg); + if (r >= 0) { + setHeadAndPropagate(node, r); + p.next = null; // help GC + failed = false; + return true; + } + } + if (nanosTimeout <= 0) + return false; + if (shouldParkAfterFailedAcquire(p, node) && + nanosTimeout > spinForTimeoutThreshold) + LockSupport.parkNanos(this, nanosTimeout); + long now = System.nanoTime(); + nanosTimeout -= now - lastTime; + lastTime = now; + if (Thread.interrupted()) + throw new InterruptedException(); + } + } finally { + if (failed) + cancelAcquire(node); + } + } + + // Main exported methods + + /** + * Attempts to acquire in exclusive mode. This method should query + * if the state of the object permits it to be acquired in the + * exclusive 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 + * 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()}. + * + * <p>The default + * 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 {@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(long arg) { + throw new UnsupportedOperationException(); + } + + /** + * Attempts to set the state to reflect a release in exclusive + * 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 {@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(long arg) { + throw new UnsupportedOperationException(); + } + + /** + * 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. + * + * <p>This method is always invoked by the thread performing + * 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. + * + * <p>The default 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 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 long tryAcquireShared(long arg) { + throw new UnsupportedOperationException(); + } + + /** + * 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 {@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(long arg) { + throw new UnsupportedOperationException(); + } + + /** + * 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 {@code true} if synchronization is held exclusively; + * {@code false} otherwise + * @throws UnsupportedOperationException if conditions are not supported + */ + protected boolean isHeldExclusively() { + throw new UnsupportedOperationException(); + } + + /** + * Acquires in exclusive mode, ignoring interrupts. Implemented + * by invoking at least once {@link #tryAcquire}, + * 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. + */ + public final void acquire(long arg) { + if (!tryAcquire(arg) && + acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) + selfInterrupt(); + } + + /** + * Acquires in exclusive mode, aborting if interrupted. + * Implemented by first checking interrupt status, then invoking + * at least once {@link #tryAcquire}, returning on + * 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. + * @throws InterruptedException if the current thread is interrupted + */ + public final void acquireInterruptibly(long arg) throws InterruptedException { + if (Thread.interrupted()) + throw new InterruptedException(); + if (!tryAcquire(arg)) + doAcquireInterruptibly(arg); + } + + /** + * Attempts to acquire in exclusive mode, aborting if interrupted, + * and failing if the given timeout elapses. Implemented by first + * checking interrupt status, then invoking at least once {@link + * #tryAcquire}, returning on success. Otherwise, the thread is + * queued, possibly repeatedly blocking and unblocking, invoking + * {@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 nanosTimeout the maximum number of nanoseconds to wait + * @return {@code true} if acquired; {@code false} if timed out + * @throws InterruptedException if the current thread is interrupted + */ + public final boolean tryAcquireNanos(long 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} + */ + public final boolean release(long arg) { + if (tryRelease(arg)) { + Node h = head; + if (h != null && h.waitStatus != 0) + unparkSuccessor(h); + return true; + } + return false; + } + + /** + * Acquires in shared mode, ignoring interrupts. Implemented by + * 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. + */ + public final void acquireShared(long arg) { + if (tryAcquireShared(arg) < 0) + doAcquireShared(arg); + } + + /** + * Acquires in shared mode, aborting if interrupted. Implemented + * by first checking interrupt status, then invoking at least once + * {@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 + * This value is conveyed to {@link #tryAcquireShared} but is + * otherwise uninterpreted and can represent anything + * you like. + * @throws InterruptedException if the current thread is interrupted + */ + public final void acquireSharedInterruptibly(long arg) throws InterruptedException { + if (Thread.interrupted()) + throw new InterruptedException(); + if (tryAcquireShared(arg) < 0) + doAcquireSharedInterruptibly(arg); + } + + /** + * Attempts to acquire in shared mode, aborting if interrupted, and + * failing if the given timeout elapses. Implemented by first + * checking interrupt status, then invoking at least once {@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 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 nanosTimeout the maximum number of nanoseconds to wait + * @return {@code true} if acquired; {@code false} if timed out + * @throws InterruptedException if the current thread is interrupted + */ + public final boolean tryAcquireSharedNanos(long 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} + */ + public final boolean releaseShared(long arg) { + if (tryReleaseShared(arg)) { + doReleaseShared(); + return true; + } + return false; + } + + // Queue inspection methods + + /** + * Queries whether any threads are waiting to acquire. Note that + * because cancellations due to interrupts and timeouts may occur + * 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 + * constant time. + * + * @return {@code true} if there may be other threads waiting to acquire + */ + public final boolean hasQueuedThreads() { + return head != tail; + } + + /** + * 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 + * constant time. + * + * @return {@code true} if there has ever been contention + */ + public final boolean hasContended() { + return head != null; + } + + /** + * Returns the first (longest-waiting) thread in the queue, or + * {@code null} if no threads are currently queued. + * + * <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 + * {@code null} if no threads are currently queued + */ + public final Thread getFirstQueuedThread() { + // handle only fast path, else relay + return (head == tail) ? null : fullGetFirstQueuedThread(); + } + + /** + * Version of getFirstQueuedThread called when fastpath fails + */ + private Thread fullGetFirstQueuedThread() { + /* + * 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. + */ + 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; + + /* + * 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. + */ + + 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. + * + * <p>This implementation traverses the queue to determine + * presence of the given thread. + * + * @param thread the thread + * @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) + throw new NullPointerException(); + for (Node p = tail; p != null; p = p.prev) + if (p.thread == thread) + return true; + 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 + * @since 1.7 + */ + /*public*/ final boolean hasQueuedPredecessors() { // android-changed + // 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 + + /** + * 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 system state, not for synchronization + * control. + * + * @return the estimated number of threads waiting to acquire + */ + public final int getQueueLength() { + int n = 0; + for (Node p = tail; p != null; p = p.prev) { + if (p.thread != null) + ++n; + } + return n; + } + + /** + * 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 + */ + public final Collection<Thread> getQueuedThreads() { + ArrayList<Thread> list = new ArrayList<Thread>(); + for (Node p = tail; p != null; p = p.prev) { + Thread t = p.thread; + if (t != null) + list.add(t); + } + return list; + } + + /** + * Returns a collection containing threads that may be waiting to + * 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() { + ArrayList<Thread> list = new ArrayList<Thread>(); + for (Node p = tail; p != null; p = p.prev) { + if (!p.isShared()) { + Thread t = p.thread; + if (t != null) + list.add(t); + } + } + return list; + } + + /** + * Returns a collection containing threads that may be waiting to + * 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() { + ArrayList<Thread> list = new ArrayList<Thread>(); + for (Node p = tail; p != null; p = p.prev) { + if (p.isShared()) { + Thread t = p.thread; + if (t != null) + list.add(t); + } + } + return list; + } + + /** + * 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 + */ + public String toString() { + long s = getState(); + String q = hasQueuedThreads() ? "non" : ""; + return super.toString() + + "[State = " + s + ", " + q + "empty queue]"; + } + + + // Internal support methods for Conditions + + /** + * Returns true if a node, always one that was initially placed on + * a condition queue, is now waiting to reacquire on sync queue. + * @param node the node + * @return true if is reacquiring + */ + final boolean isOnSyncQueue(Node node) { + if (node.waitStatus == Node.CONDITION || node.prev == null) + return false; + if (node.next != null) // If has successor, it must be on queue + return true; + /* + * node.prev can be non-null, but not yet on queue because + * the CAS to place it on queue can fail. So we have to + * traverse from tail to make sure it actually made it. It + * will always be near the tail in calls to this method, and + * unless the CAS failed (which is unlikely), it will be + * there, so we hardly ever traverse much. + */ + return findNodeFromTail(node); + } + + /** + * Returns true if node is on sync queue by searching backwards from tail. + * Called only when needed by isOnSyncQueue. + * @return true if present + */ + private boolean findNodeFromTail(Node node) { + Node t = tail; + for (;;) { + if (t == node) + return true; + if (t == null) + return false; + t = t.prev; + } + } + + /** + * 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 + * cancelled before signal). + */ + final boolean transferForSignal(Node node) { + /* + * If cannot change waitStatus, the node has been cancelled. + */ + if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) + return false; + + /* + * Splice onto queue and try to set waitStatus of predecessor to + * indicate that thread is (probably) waiting. If cancelled or + * attempt to set waitStatus fails, wake up to resync (in which + * case the waitStatus can be transiently and harmlessly wrong). + */ + Node p = enq(node); + int ws = p.waitStatus; + if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) + LockSupport.unpark(node.thread); + return true; + } + + /** + * 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 + */ + final boolean transferAfterCancelledWait(Node node) { + if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) { + enq(node); + return true; + } + /* + * If we lost out to a signal(), then we can't proceed + * until it finishes its enq(). Cancelling during an + * incomplete transfer is both rare and transient, so just + * spin. + */ + while (!isOnSyncQueue(node)) + Thread.yield(); + return false; + } + + /** + * 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 long fullyRelease(Node node) { + boolean failed = true; + try { + long savedState = getState(); + if (release(savedState)) { + failed = false; + return savedState; + } else { + throw new IllegalMonitorStateException(); + } + } finally { + if (failed) + node.waitStatus = Node.CANCELLED; + } + } + + // Instrumentation methods for conditions + + /** + * Queries whether the given ConditionObject + * uses this synchronizer as its lock. + * + * @param condition the condition + * @return <tt>true</tt> if owned + * @throws NullPointerException if the condition is null + */ + public final boolean owns(ConditionObject condition) { + if (condition == null) + throw new NullPointerException(); + return condition.isOwnedBy(this); + } + + /** + * Queries whether any threads are waiting on the given condition + * associated with this synchronizer. 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 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 + * @throws IllegalArgumentException if the given condition is + * 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"); + return condition.hasWaiters(); + } + + /** + * Returns an estimate of the number of threads waiting on the + * given condition associated with this synchronizer. Note that + * because timeouts and interrupts may occur at any time, the + * 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 + * @throws IllegalArgumentException if the given condition is + * 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"); + return condition.getWaitQueueLength(); + } + + /** + * Returns a collection containing those threads that may be + * waiting on the given condition associated with this + * 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. + * + * @param condition the condition + * @return the collection of threads + * @throws IllegalMonitorStateException if exclusive synchronization + * is not held + * @throws IllegalArgumentException if the given condition is + * not associated with this synchronizer + * @throws NullPointerException if the condition is null + */ + public final Collection<Thread> getWaitingThreads(ConditionObject condition) { + if (!owns(condition)) + throw new IllegalArgumentException("Not owner"); + return condition.getWaitingThreads(); + } + + /** + * Condition implementation for a {@link + * AbstractQueuedLongSynchronizer} serving as the basis of a {@link + * Lock} implementation. + * + * <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>AbstractQueuedLongSynchronizer</tt>. + * + * <p>This class is Serializable, but all fields are transient, + * so deserialized conditions have no waiters. + * + * @since 1.6 + */ + public class ConditionObject implements Condition, java.io.Serializable { + private static final long serialVersionUID = 1173984872572414699L; + /** First node of condition queue. */ + private transient Node firstWaiter; + /** Last node of condition queue. */ + private transient Node lastWaiter; + + /** + * Creates a new <tt>ConditionObject</tt> instance. + */ + public ConditionObject() { } + + // Internal methods + + /** + * Adds a new waiter to wait queue. + * @return its new wait node + */ + private Node addConditionWaiter() { + Node t = lastWaiter; + // 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 + t.nextWaiter = node; + lastWaiter = node; + return node; + } + + /** + * 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) + lastWaiter = null; + first.nextWaiter = null; + } while (!transferForSignal(first) && + (first = firstWaiter) != null); + } + + /** + * Removes and transfers all nodes. + * @param first (non-null) the first node on condition queue + */ + private void doSignalAll(Node first) { + lastWaiter = firstWaiter = null; + do { + Node next = first.nextWaiter; + first.nextWaiter = null; + transferForSignal(first); + first = next; + } 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 {@code false} + */ + public final void signal() { + 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 {@code false} + */ + public final void signalAll() { + if (!isHeldExclusively()) + throw new IllegalMonitorStateException(); + Node first = firstWaiter; + 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> Reacquire by invoking specialized version of + * {@link #acquire} with saved state as argument. + * </ol> + */ + public final void awaitUninterruptibly() { + Node node = addConditionWaiter(); + long savedState = fullyRelease(node); + boolean interrupted = false; + while (!isOnSyncQueue(node)) { + LockSupport.park(this); + if (Thread.interrupted()) + interrupted = true; + } + if (acquireQueued(node, savedState) || interrupted) + selfInterrupt(); + } + + /* + * For interruptible waits, we need to track whether to throw + * InterruptedException, if interrupted while blocked on + * condition, versus reinterrupt current thread, if + * interrupted while blocked waiting to re-acquire. + */ + + /** Mode meaning to reinterrupt on exit from wait */ + private static final int REINTERRUPT = 1; + /** Mode meaning to throw InterruptedException on exit from wait */ + private static final int THROW_IE = -1; + + /** + * 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) : + 0; + } + + /** + * Throws InterruptedException, reinterrupts current thread, or + * does nothing, depending on mode. + */ + private void reportInterruptAfterWait(int interruptMode) + throws InterruptedException { + if (interruptMode == THROW_IE) + throw new InterruptedException(); + else if (interruptMode == REINTERRUPT) + 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> Reacquire by invoking specialized version of + * {@link #acquire} with saved state as argument. + * <li> If interrupted while blocked in step 4, throw InterruptedException. + * </ol> + */ + public final void await() throws InterruptedException { + if (Thread.interrupted()) + throw new InterruptedException(); + Node node = addConditionWaiter(); + long savedState = fullyRelease(node); + int interruptMode = 0; + while (!isOnSyncQueue(node)) { + LockSupport.park(this); + if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) + break; + } + if (acquireQueued(node, savedState) && interruptMode != THROW_IE) + interruptMode = REINTERRUPT; + if (node.nextWaiter != null) // clean up if cancelled + unlinkCancelledWaiters(); + if (interruptMode != 0) + reportInterruptAfterWait(interruptMode); + } + + /** + * 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> Reacquire by invoking specialized version of + * {@link #acquire} with saved state as argument. + * <li> If interrupted while blocked in step 4, throw InterruptedException. + * </ol> + */ + public final long awaitNanos(long nanosTimeout) throws InterruptedException { + if (Thread.interrupted()) + throw new InterruptedException(); + Node node = addConditionWaiter(); + long savedState = fullyRelease(node); + long lastTime = System.nanoTime(); + int interruptMode = 0; + while (!isOnSyncQueue(node)) { + if (nanosTimeout <= 0L) { + transferAfterCancelledWait(node); + break; + } + LockSupport.parkNanos(this, 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); + } + + /** + * 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> 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. + * </ol> + */ + public final boolean awaitUntil(Date deadline) throws InterruptedException { + if (deadline == null) + throw new NullPointerException(); + long abstime = deadline.getTime(); + if (Thread.interrupted()) + throw new InterruptedException(); + Node node = addConditionWaiter(); + long savedState = fullyRelease(node); + boolean timedout = false; + int interruptMode = 0; + while (!isOnSyncQueue(node)) { + if (System.currentTimeMillis() > abstime) { + timedout = transferAfterCancelledWait(node); + break; + } + LockSupport.parkUntil(this, abstime); + if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) + break; + } + 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. + * <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> 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. + * </ol> + */ + public final boolean await(long time, TimeUnit unit) throws InterruptedException { + if (unit == null) + throw new NullPointerException(); + long nanosTimeout = unit.toNanos(time); + if (Thread.interrupted()) + throw new InterruptedException(); + Node node = addConditionWaiter(); + long savedState = fullyRelease(node); + long lastTime = System.nanoTime(); + boolean timedout = false; + int interruptMode = 0; + while (!isOnSyncQueue(node)) { + if (nanosTimeout <= 0L) { + timedout = transferAfterCancelledWait(node); + break; + } + if (nanosTimeout >= spinForTimeoutThreshold) + LockSupport.parkNanos(this, 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 !timedout; + } + + // support for instrumentation + + /** + * Returns true if this condition was created by the given + * synchronization object. + * + * @return {@code true} if owned + */ + final boolean isOwnedBy(AbstractQueuedLongSynchronizer sync) { + return sync == AbstractQueuedLongSynchronizer.this; + } + + /** + * Queries whether any threads are waiting on this condition. + * Implements {@link AbstractQueuedLongSynchronizer#hasWaiters}. + * + * @return {@code true} if there are any waiting threads + * @throws IllegalMonitorStateException if {@link #isHeldExclusively} + * returns {@code false} + */ + protected final boolean hasWaiters() { + if (!isHeldExclusively()) + throw new IllegalMonitorStateException(); + for (Node w = firstWaiter; w != null; w = w.nextWaiter) { + if (w.waitStatus == Node.CONDITION) + return true; + } + return false; + } + + /** + * Returns an estimate of the number of threads waiting on + * this condition. + * Implements {@link AbstractQueuedLongSynchronizer#getWaitQueueLength}. + * + * @return the estimated number of waiting threads + * @throws IllegalMonitorStateException if {@link #isHeldExclusively} + * returns {@code false} + */ + protected final int getWaitQueueLength() { + if (!isHeldExclusively()) + throw new IllegalMonitorStateException(); + int n = 0; + for (Node w = firstWaiter; w != null; w = w.nextWaiter) { + if (w.waitStatus == Node.CONDITION) + ++n; + } + return n; + } + + /** + * Returns a collection containing those threads that may be + * waiting on this Condition. + * Implements {@link AbstractQueuedLongSynchronizer#getWaitingThreads}. + * + * @return the collection of threads + * @throws IllegalMonitorStateException if {@link #isHeldExclusively} + * returns {@code false} + */ + protected final Collection<Thread> getWaitingThreads() { + if (!isHeldExclusively()) + throw new IllegalMonitorStateException(); + ArrayList<Thread> list = new ArrayList<Thread>(); + for (Node w = firstWaiter; w != null; w = w.nextWaiter) { + if (w.waitStatus == Node.CONDITION) { + Thread t = w.thread; + if (t != null) + list.add(t); + } + } + return list; + } + } + + /** + * Setup to support compareAndSet. We need to natively implement + * this here: For the sake of permitting future enhancements, we + * cannot explicitly subclass AtomicLong, which would be + * efficient and useful otherwise. So, as the lesser of evils, we + * natively implement using hotspot intrinsics API. And while we + * are at it, we do the same for other CASable fields (which could + * otherwise be done with atomic field updaters). + */ + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final long stateOffset; + private static final long headOffset; + private static final long tailOffset; + private static final long waitStatusOffset; + private static final long nextOffset; + + static { + try { + stateOffset = unsafe.objectFieldOffset + (AbstractQueuedLongSynchronizer.class.getDeclaredField("state")); + headOffset = unsafe.objectFieldOffset + (AbstractQueuedLongSynchronizer.class.getDeclaredField("head")); + tailOffset = unsafe.objectFieldOffset + (AbstractQueuedLongSynchronizer.class.getDeclaredField("tail")); + waitStatusOffset = unsafe.objectFieldOffset + (Node.class.getDeclaredField("waitStatus")); + nextOffset = unsafe.objectFieldOffset + (Node.class.getDeclaredField("next")); + + } catch (Exception ex) { throw new Error(ex); } + } + + /** + * 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. + */ + private final boolean compareAndSetTail(Node expect, Node update) { + return unsafe.compareAndSwapObject(this, tailOffset, expect, update); + } + + /** + * CAS waitStatus field of a node. + */ + private final static boolean compareAndSetWaitStatus(Node node, + int expect, + int update) { + 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/AbstractQueuedSynchronizer.java b/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java index 8b9821e..66498e3 100644 --- a/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java +++ b/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java @@ -94,6 +94,12 @@ import sun.misc.Unsafe; * means of using this class. All other methods are declared * <tt>final</tt> because they cannot be independently varied. * + * <p>You may also find the inherited methods from {@link + * AbstractOwnableSynchronizer} useful to keep track of the thread + * owning an exclusive synchronizer. You are encouraged to use them + * -- this enables monitoring and diagnostic tools to assist users in + * determining which threads hold locks. + * * <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: @@ -119,8 +125,9 @@ import sun.misc.Unsafe; * 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. + * to return <tt>false</tt> if {@link #hasQueuedPredecessors} (a method + * specifically designed to be used by fair synchronizers) returns + * <tt>true</tt>. Other variations are possible. * * <p>Throughput and scalability are generally highest for the * default barging (also known as <em>greedy</em>, @@ -152,7 +159,10 @@ import sun.misc.Unsafe; * * <p>Here is a non-reentrant mutual exclusion lock class that uses * the value zero to represent the unlocked state, and one to - * represent the locked state. It also supports conditions and exposes + * represent the locked state. While a non-reentrant lock + * does not strictly require recording of the current owner + * thread, this class does so anyway to make usage easier to monitor. + * It also supports conditions and exposes * one of the instrumentation methods: * * <pre> @@ -168,13 +178,18 @@ import sun.misc.Unsafe; * // Acquire the lock if state is zero * public boolean tryAcquire(int acquires) { * assert acquires == 1; // Otherwise unused - * return compareAndSetState(0, 1); + * if (compareAndSetState(0, 1)) { + * setExclusiveOwnerThread(Thread.currentThread()); + * return true; + * } + * return false; * } * * // Release the lock by setting state to zero * protected boolean tryRelease(int releases) { * assert releases == 1; // Otherwise unused * if (getState() == 0) throw new IllegalMonitorStateException(); + * setExclusiveOwnerThread(null); * setState(0); * return true; * } @@ -787,7 +802,7 @@ public abstract class AbstractQueuedSynchronizer * @return {@code true} if interrupted */ private final boolean parkAndCheckInterrupt() { - LockSupport.park(); + LockSupport.park(this); return Thread.interrupted(); } @@ -882,7 +897,7 @@ public abstract class AbstractQueuedSynchronizer return false; if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold) - LockSupport.parkNanos(nanosTimeout); + LockSupport.parkNanos(this, nanosTimeout); long now = System.nanoTime(); nanosTimeout -= now - lastTime; lastTime = now; @@ -986,7 +1001,7 @@ public abstract class AbstractQueuedSynchronizer return false; if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold) - LockSupport.parkNanos(nanosTimeout); + LockSupport.parkNanos(this, nanosTimeout); long now = System.nanoTime(); nanosTimeout -= now - lastTime; lastTime = now; @@ -1458,8 +1473,10 @@ public abstract class AbstractQueuedSynchronizer * @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 + * @since 1.7 + * @hide */ - final boolean hasQueuedPredecessors() { + public 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. @@ -1926,7 +1943,7 @@ public abstract class AbstractQueuedSynchronizer int savedState = fullyRelease(node); boolean interrupted = false; while (!isOnSyncQueue(node)) { - LockSupport.park(); + LockSupport.park(this); if (Thread.interrupted()) interrupted = true; } @@ -1993,7 +2010,7 @@ public abstract class AbstractQueuedSynchronizer int savedState = fullyRelease(node); int interruptMode = 0; while (!isOnSyncQueue(node)) { - LockSupport.park(); + LockSupport.park(this); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; } @@ -2040,7 +2057,7 @@ public abstract class AbstractQueuedSynchronizer transferAfterCancelledWait(node); break; } - LockSupport.parkNanos(nanosTimeout); + LockSupport.parkNanos(this, nanosTimeout); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; @@ -2094,7 +2111,7 @@ public abstract class AbstractQueuedSynchronizer timedout = transferAfterCancelledWait(node); break; } - LockSupport.parkUntil(abstime); + LockSupport.parkUntil(this, abstime); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; } @@ -2146,7 +2163,7 @@ public abstract class AbstractQueuedSynchronizer break; } if (nanosTimeout >= spinForTimeoutThreshold) - LockSupport.parkNanos(nanosTimeout); + LockSupport.parkNanos(this, nanosTimeout); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; long now = System.nanoTime(); 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 03be58f..d75de51 100644 --- a/concurrent/src/main/java/java/util/concurrent/locks/Condition.java +++ b/concurrent/src/main/java/java/util/concurrent/locks/Condition.java @@ -421,6 +421,15 @@ 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 {@code await}. + * + * <p><b>Implementation Considerations</b> + * + * <p>The current thread is assumed to hold the lock associated + * with this {@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 thrown + * (such as {@link IllegalMonitorStateException}) and the + * implementation must document that fact. */ void signal(); @@ -430,6 +439,15 @@ 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 {@code await}. + * + * <p><b>Implementation Considerations</b> + * + * <p>The current thread is assumed to hold the lock associated + * with this {@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 thrown + * (such as {@link IllegalMonitorStateException}) and the + * implementation must document that fact. */ void signalAll(); } 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 b55b874..ac9a94f 100644 --- a/concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java +++ b/concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java @@ -36,6 +36,15 @@ import sun.misc.Unsafe; * spinning, but must be paired with an {@code unpark} to be * effective. * + * <p>The three forms of {@code park} each also support a + * {@code blocker} object parameter. This object is recorded while + * the thread is blocked to permit monitoring and diagnostic tools to + * identify the reasons that threads are blocked. (Such tools may + * access blockers using method {@link #getBlocker}.) The use of these + * forms rather than the original forms without this parameter is + * strongly encouraged. The normal argument to supply as a + * {@code blocker} within a lock implementation is {@code this}. + * * <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. The {@code park} @@ -78,13 +87,25 @@ import sun.misc.Unsafe; * } * }}</pre> */ -@SuppressWarnings("all") + public class LockSupport { private LockSupport() {} // Cannot be instantiated. - // BEGIN android-changed - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; - // END android-changed + // Hotspot implementation via intrinsics API + private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed + private static final long parkBlockerOffset; + + static { + try { + parkBlockerOffset = unsafe.objectFieldOffset + (java.lang.Thread.class.getDeclaredField("parkBlocker")); + } catch (Exception ex) { throw new Error(ex); } + } + + private static void setBlocker(Thread t, Object arg) { + // Even though volatile, hotspot doesn't need a write barrier here. + unsafe.putObject(t, parkBlockerOffset, arg); + } /** * Makes available the permit for the given thread, if it @@ -106,6 +127,136 @@ 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: + * + * <ul> + * <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, + * for example, the interrupt status of the thread upon return. + * + * @param blocker the synchronization object responsible for this + * thread parking + * @since 1.6 + */ + public static void park(Object blocker) { + Thread t = Thread.currentThread(); + setBlocker(t, blocker); + unsafe.park(false, 0L); + setBlocker(t, null); + } + + /** + * 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: + * + * <ul> + * <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, + * for example, the interrupt status of the thread, or the elapsed time + * upon return. + * + * @param blocker the synchronization object responsible for this + * thread parking + * @param nanos the maximum number of nanoseconds to wait + * @since 1.6 + */ + public static void parkNanos(Object blocker, long nanos) { + if (nanos > 0) { + Thread t = Thread.currentThread(); + setBlocker(t, blocker); + unsafe.park(false, nanos); + setBlocker(t, null); + } + } + + /** + * 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: + * + * <ul> + * <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, + * for example, the interrupt status of the thread, or the current time + * upon return. + * + * @param blocker the synchronization object responsible for this + * thread parking + * @param deadline the absolute time, in milliseconds from the Epoch, + * to wait until + * @since 1.6 + */ + public static void parkUntil(Object blocker, long deadline) { + Thread t = Thread.currentThread(); + setBlocker(t, blocker); + unsafe.park(true, deadline); + setBlocker(t, null); + } + + /** + * Returns the blocker object supplied to the most recent + * invocation of a park method that has not yet unblocked, or null + * if not blocked. The value returned is just a momentary + * snapshot -- the thread may have since unblocked or blocked on a + * different blocker object. + * + * @return the blocker + * @since 1.6 + */ + public static Object getBlocker(Thread t) { + return unsafe.getObjectVolatile(t, parkBlockerOffset); + } + + /** + * 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 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 c923944..bfaff06 100644 --- a/concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java +++ b/concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java @@ -1161,6 +1161,32 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab "[Locked by thread " + o.getName() + "]"); } + /** + * Queries if this write lock is held by the current thread. + * Identical in effect to {@link + * ReentrantReadWriteLock#isWriteLockedByCurrentThread}. + * + * @return {@code true} if the current thread holds this lock and + * {@code false} otherwise + * @since 1.6 + */ + public boolean isHeldByCurrentThread() { + return sync.isHeldExclusively(); + } + + /** + * Queries the number of holds on this write lock by the current + * thread. A thread has a hold on a lock for each lock action + * that is not matched by an unlock action. Identical in effect + * to {@link ReentrantReadWriteLock#getWriteHoldCount}. + * + * @return the number of holds on this lock by the current thread, + * or zero if this lock is not held by the current thread + * @since 1.6 + */ + public int getHoldCount() { + return sync.getWriteHoldCount(); + } } // Instrumentation and status @@ -1236,6 +1262,19 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab } /** + * Queries the number of reentrant read holds on this lock by the + * current thread. A reader 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 the read lock by the current thread, + * or zero if the read lock is not held by the current thread + * @since 1.6 + */ + public int getReadHoldCount() { + return sync.getReadHoldCount(); + } + + /** * Returns a collection containing threads that may be waiting to * acquire the write lock. Because the actual set of threads may * change dynamically while constructing this result, the returned 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 index 801ee9b..860acdd 100644 --- a/concurrent/src/main/java/java/util/concurrent/locks/package-info.java +++ b/concurrent/src/main/java/java/util/concurrent/locks/package-info.java @@ -34,10 +34,16 @@ * * <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. + * 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. Both extend class {@link + * java.util.concurrent.locks.AbstractOwnableSynchronizer}, a simple + * class that helps record the thread currently holding exclusive + * synchronization. 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 */ diff --git a/concurrent/src/main/java/java/util/concurrent/package-info.java b/concurrent/src/main/java/java/util/concurrent/package-info.java index d49ef25..dd3a22d 100644 --- a/concurrent/src/main/java/java/util/concurrent/package-info.java +++ b/concurrent/src/main/java/java/util/concurrent/package-info.java @@ -41,6 +41,10 @@ * 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> @@ -59,6 +63,13 @@ * assists in coordinating the processing of groups of * asynchronous tasks. * + * <p>Class {@link java.util.concurrent.ForkJoinPool} provides an + * Executor primarily designed for processing instances of {@link + * java.util.concurrent.ForkJoinTask} and its subclasses. These + * classes employ a work-stealing scheduler that attains high + * throughput for tasks conforming to restrictions that often hold in + * computation-intensive parallel processing. + * * <h2>Queues</h2> * * The {@link java.util.concurrent.ConcurrentLinkedQueue} class @@ -77,6 +88,18 @@ * for producer-consumer, messaging, parallel tasking, and * related concurrent designs. * + * <p> Extended interface {@link java.util.concurrent.TransferQueue}, + * and implementation {@link java.util.concurrent.LinkedTransferQueue} + * introduce a synchronous {@code transfer} method (along with related + * features) in which a producer may optionally block awaiting its + * consumer. + * + * <p>The {@link java.util.concurrent.BlockingDeque} interface + * extends {@code BlockingQueue} to support both FIFO and LIFO + * (stack-based) operations. + * Class {@link java.util.concurrent.LinkedBlockingDeque} + * provides an implementation. + * * <h2>Timing</h2> * * The {@link java.util.concurrent.TimeUnit} class provides @@ -97,28 +120,45 @@ * * <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. + * Five classes aid common special-purpose synchronization idioms. + * <ul> + * + * <li>{@link java.util.concurrent.Semaphore} is a classic concurrency tool. + * + * <li>{@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. + * + * <li>A {@link java.util.concurrent.CyclicBarrier} is a resettable + * multiway synchronization point useful in some styles of parallel + * programming. + * + * <li>A {@link java.util.concurrent.Phaser} provides + * a more flexible form of barrier that may be used to control phased + * computation among multiple threads. + * + * <li>An {@link java.util.concurrent.Exchanger} allows two threads to + * exchange objects at a rendezvous point, and is useful in several + * pipeline designs. + * + * </ul> * * <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.ConcurrentSkipListMap}, + * {@link java.util.concurrent.ConcurrentSkipListSet}, * {@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. + * {@code HashMap}, and a {@code ConcurrentSkipListMap} is normally + * preferable to a synchronized {@code TreeMap}. + * 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 @@ -216,7 +256,8 @@ * 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} + * <li>Actions prior to calling {@code CyclicBarrier.await} and + * {@code Phaser.awaitAdvance} (as well as its variants) * <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} |