From 10467906c4146ce006bfac9e78fe0dba025c2768 Mon Sep 17 00:00:00 2001 From: Lance Andersen Date: Tue, 11 Jan 2011 12:36:43 -0500 Subject: [PATCH 1/6] 7000693: java.sql.Timestamp compareTo() issues using low values Reviewed-by: okutsu --- jdk/src/share/classes/java/sql/Timestamp.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jdk/src/share/classes/java/sql/Timestamp.java b/jdk/src/share/classes/java/sql/Timestamp.java index 411c04eb19e..f92a89a8778 100644 --- a/jdk/src/share/classes/java/sql/Timestamp.java +++ b/jdk/src/share/classes/java/sql/Timestamp.java @@ -473,7 +473,9 @@ public class Timestamp extends java.util.Date { * @since 1.4 */ public int compareTo(Timestamp ts) { - int i = super.compareTo(ts); + long thisTime = this.getTime(); + long anotherTime = ts.getTime(); + int i = (thisTime ts.nanos) { return 1; From 259f06de383a1723ea03e811d86e8e23e9abfe67 Mon Sep 17 00:00:00 2001 From: Stuart Marks Date: Tue, 11 Jan 2011 13:42:34 -0800 Subject: [PATCH 2/6] 7011095: revert diamond changes from 6880112 that occur in method args Reviewed-by: darcy, alanb --- .../classes/com/sun/java/util/jar/pack/BandStructure.java | 2 +- jdk/src/share/classes/java/io/ObjectStreamClass.java | 4 ++-- jdk/src/share/classes/java/lang/StringCoding.java | 2 +- jdk/src/share/classes/java/util/logging/Logger.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/jdk/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java b/jdk/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java index 3a154a088b9..473d51108c0 100644 --- a/jdk/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java @@ -1704,7 +1704,7 @@ class BandStructure { for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { assert(attrIndexLimit[i] == 0); attrIndexLimit[i] = 32; // just for the sake of predefs. - attrDefs.set(i, new ArrayList<>(Collections.nCopies( + attrDefs.set(i, new ArrayList(Collections.nCopies( attrIndexLimit[i], (Attribute.Layout)null))); } diff --git a/jdk/src/share/classes/java/io/ObjectStreamClass.java b/jdk/src/share/classes/java/io/ObjectStreamClass.java index 2d3e2055860..74608db0c1c 100644 --- a/jdk/src/share/classes/java/io/ObjectStreamClass.java +++ b/jdk/src/share/classes/java/io/ObjectStreamClass.java @@ -329,7 +329,7 @@ public class ObjectStreamClass implements Serializable { entry = th; } if (future.set(entry)) { - Caches.localDescs.put(key, new SoftReference<>(entry)); + Caches.localDescs.put(key, new SoftReference(entry)); } else { // nested lookup call already set future entry = future.get(); @@ -2118,7 +2118,7 @@ public class ObjectStreamClass implements Serializable { entry = th; } future.set(entry); - Caches.reflectors.put(key, new SoftReference<>(entry)); + Caches.reflectors.put(key, new SoftReference(entry)); } if (entry instanceof FieldReflector) { diff --git a/jdk/src/share/classes/java/lang/StringCoding.java b/jdk/src/share/classes/java/lang/StringCoding.java index 42849479fcc..4043d5d2a89 100644 --- a/jdk/src/share/classes/java/lang/StringCoding.java +++ b/jdk/src/share/classes/java/lang/StringCoding.java @@ -67,7 +67,7 @@ class StringCoding { } private static void set(ThreadLocal> tl, T ob) { - tl.set(new SoftReference<>(ob)); + tl.set(new SoftReference(ob)); } // Trim the given byte array to the given length diff --git a/jdk/src/share/classes/java/util/logging/Logger.java b/jdk/src/share/classes/java/util/logging/Logger.java index 4d822de4792..b75caa56005 100644 --- a/jdk/src/share/classes/java/util/logging/Logger.java +++ b/jdk/src/share/classes/java/util/logging/Logger.java @@ -1426,7 +1426,7 @@ public class Logger { // we didn't have a previous parent ref = manager.new LoggerWeakRef(this); } - ref.setParentRef(new WeakReference<>(parent)); + ref.setParentRef(new WeakReference(parent)); parent.kids.add(ref); // As a result of the reparenting, the effective level From 677a39996a45f738d0488bc0b69d54581d4cb373 Mon Sep 17 00:00:00 2001 From: Doug Lea Date: Wed, 12 Jan 2011 14:40:36 +0000 Subject: [PATCH 3/6] 7005424: Resync java.util.concurrent classes with Dougs CVS - Jan 2011 Reviewed-by: dholmes, chegar, mduigou --- .../share/classes/java/util/Collections.java | 6 +- .../share/classes/java/util/LinkedList.java | 8 +- .../util/concurrent/ArrayBlockingQueue.java | 436 ++++--- .../concurrent/ConcurrentLinkedDeque.java | 13 +- .../concurrent/ConcurrentLinkedQueue.java | 3 + .../concurrent/ConcurrentSkipListMap.java | 167 +-- .../util/concurrent/CopyOnWriteArrayList.java | 14 +- .../java/util/concurrent/ForkJoinPool.java | 227 ++-- .../java/util/concurrent/ForkJoinTask.java | 286 ++--- .../util/concurrent/ForkJoinWorkerThread.java | 274 ++-- .../util/concurrent/LinkedBlockingDeque.java | 2 + .../util/concurrent/LinkedBlockingQueue.java | 66 +- .../util/concurrent/LinkedTransferQueue.java | 121 +- .../classes/java/util/concurrent/Phaser.java | 1134 +++++++++-------- .../concurrent/PriorityBlockingQueue.java | 601 +++++++-- .../ScheduledThreadPoolExecutor.java | 6 +- .../util/concurrent/SynchronousQueue.java | 134 +- .../util/concurrent/ThreadPoolExecutor.java | 41 +- .../concurrent/atomic/AtomicIntegerArray.java | 116 +- .../concurrent/atomic/AtomicLongArray.java | 124 +- .../atomic/AtomicMarkableReference.java | 96 +- .../atomic/AtomicReferenceArray.java | 79 +- .../atomic/AtomicStampedReference.java | 111 +- .../java/util/concurrent/locks/Condition.java | 66 +- jdk/test/ProblemList.txt | 4 +- .../util/WeakHashMap/GCDuringIteration.java | 62 +- .../CancelledProducerConsumerLoops.java | 34 +- .../MultipleProducersSingleConsumerLoops.java | 30 +- .../BlockingQueue/ProducerConsumerLoops.java | 30 +- .../SingleProducerMultipleConsumerLoops.java | 30 +- .../IteratorWeakConsistency.java | 4 +- .../concurrent/Executors/AutoShutdown.java | 1 + .../java/util/concurrent/Phaser/Basic.java | 33 +- .../concurrent/Phaser/FickleRegister.java | 150 +++ .../util/concurrent/Phaser/PhaseOverflow.java | 158 +++ .../concurrent/Phaser/TieredArriveLoops.java | 117 ++ .../ThreadPoolExecutor/CoreThreadTimeOut.java | 62 +- 37 files changed, 3014 insertions(+), 1832 deletions(-) create mode 100644 jdk/test/java/util/concurrent/Phaser/FickleRegister.java create mode 100644 jdk/test/java/util/concurrent/Phaser/PhaseOverflow.java create mode 100644 jdk/test/java/util/concurrent/Phaser/TieredArriveLoops.java diff --git a/jdk/src/share/classes/java/util/Collections.java b/jdk/src/share/classes/java/util/Collections.java index 08f8adbe5b2..8be24796f7d 100644 --- a/jdk/src/share/classes/java/util/Collections.java +++ b/jdk/src/share/classes/java/util/Collections.java @@ -1452,10 +1452,10 @@ public class Collections { * when o is a Map.Entry, and calls o.setValue. */ public boolean containsAll(Collection coll) { - Iterator it = coll.iterator(); - while (it.hasNext()) - if (!contains(it.next())) // Invokes safe contains() above + for (Object e : coll) { + if (!contains(e)) // Invokes safe contains() above return false; + } return true; } public boolean equals(Object o) { diff --git a/jdk/src/share/classes/java/util/LinkedList.java b/jdk/src/share/classes/java/util/LinkedList.java index fbf8f5f92ce..e4d0ddc0787 100644 --- a/jdk/src/share/classes/java/util/LinkedList.java +++ b/jdk/src/share/classes/java/util/LinkedList.java @@ -26,9 +26,9 @@ package java.util; /** - * Linked list implementation of the {@link List} and {@link Deque} interfaces. - * Implements all optional operations, and permits all elements (including - * {@code null}). + * Doubly-linked list implementation of the {@code List} and {@code Deque} + * interfaces. Implements all optional list operations, and permits all + * elements (including {@code null}). * *

All of the operations perform as could be expected for a doubly-linked * list. Operations that index into the list will traverse the list from @@ -249,7 +249,7 @@ public class LinkedList * @return the last element in this list * @throws NoSuchElementException if this list is empty */ - public E getLast() { + public E getLast() { final Node l = last; if (l == null) throw new NoSuchElementException(); diff --git a/jdk/src/share/classes/java/util/concurrent/ArrayBlockingQueue.java b/jdk/src/share/classes/java/util/concurrent/ArrayBlockingQueue.java index 0690c8216e9..4f4b9d96af6 100644 --- a/jdk/src/share/classes/java/util/concurrent/ArrayBlockingQueue.java +++ b/jdk/src/share/classes/java/util/concurrent/ArrayBlockingQueue.java @@ -49,14 +49,14 @@ import java.util.*; *

This is a classic "bounded buffer", in which a * fixed-sized array holds elements inserted by producers and * extracted by consumers. Once created, the capacity cannot be - * increased. Attempts to put an element into a full queue - * will result in the operation blocking; attempts to take an + * changed. Attempts to {@code put} an element into a full queue + * will result in the operation blocking; attempts to {@code take} an * element from an empty queue will similarly block. * - *

This class supports an optional fairness policy for ordering + *

This class supports an optional fairness policy for ordering * waiting producer and consumer threads. By default, this ordering * is not guaranteed. However, a queue constructed with fairness set - * to true grants threads access in FIFO order. Fairness + * to {@code true} grants threads access in FIFO order. Fairness * generally decreases throughput but reduces variability and avoids * starvation. * @@ -83,14 +83,17 @@ public class ArrayBlockingQueue extends AbstractQueue */ private static final long serialVersionUID = -817911632652898426L; - /** The queued items */ - private final E[] items; - /** items index for next take, poll or remove */ - private int takeIndex; - /** items index for next put, offer, or add. */ - private int putIndex; - /** Number of items in the queue */ - private int count; + /** The queued items */ + final Object[] items; + + /** items index for next take, poll, peek or remove */ + int takeIndex; + + /** items index for next put, offer, or add */ + int putIndex; + + /** Number of elements in the queue */ + int count; /* * Concurrency control uses the classic two-condition algorithm @@ -98,7 +101,7 @@ public class ArrayBlockingQueue extends AbstractQueue */ /** Main lock guarding all access */ - private final ReentrantLock lock; + final ReentrantLock lock; /** Condition for waiting takes */ private final Condition notEmpty; /** Condition for waiting puts */ @@ -110,7 +113,36 @@ public class ArrayBlockingQueue extends AbstractQueue * Circularly increment i. */ final int inc(int i) { - return (++i == items.length)? 0 : i; + return (++i == items.length) ? 0 : i; + } + + /** + * Circularly decrement i. + */ + final int dec(int i) { + return ((i == 0) ? items.length : i) - 1; + } + + @SuppressWarnings("unchecked") + static E cast(Object item) { + return (E) item; + } + + /** + * Returns item at index i. + */ + final E itemAt(int i) { + return this.cast(items[i]); + } + + /** + * Throws NullPointerException if argument is null. + * + * @param v the element + */ + private static void checkNotNull(Object v) { + if (v == null) + throw new NullPointerException(); } /** @@ -129,8 +161,8 @@ public class ArrayBlockingQueue extends AbstractQueue * Call only when holding lock. */ private E extract() { - final E[] items = this.items; - E x = items[takeIndex]; + final Object[] items = this.items; + E x = this.cast(items[takeIndex]); items[takeIndex] = null; takeIndex = inc(takeIndex); --count; @@ -139,11 +171,12 @@ public class ArrayBlockingQueue extends AbstractQueue } /** - * Utility for remove and iterator.remove: Delete item at position i. + * Deletes item at position i. + * Utility for remove and iterator.remove. * Call only when holding lock. */ void removeAt(int i) { - final E[] items = this.items; + final Object[] items = this.items; // if removing front item, just advance if (i == takeIndex) { items[takeIndex] = null; @@ -167,69 +200,82 @@ public class ArrayBlockingQueue extends AbstractQueue } /** - * Creates an ArrayBlockingQueue with the given (fixed) + * Creates an {@code ArrayBlockingQueue} with the given (fixed) * capacity and default access policy. * * @param capacity the capacity of this queue - * @throws IllegalArgumentException if capacity is less than 1 + * @throws IllegalArgumentException if {@code capacity < 1} */ public ArrayBlockingQueue(int capacity) { this(capacity, false); } /** - * Creates an ArrayBlockingQueue with the given (fixed) + * Creates an {@code ArrayBlockingQueue} with the given (fixed) * capacity and the specified access policy. * * @param capacity the capacity of this queue - * @param fair if true then queue accesses for threads blocked + * @param fair if {@code true} then queue accesses for threads blocked * on insertion or removal, are processed in FIFO order; - * if false the access order is unspecified. - * @throws IllegalArgumentException if capacity is less than 1 + * if {@code false} the access order is unspecified. + * @throws IllegalArgumentException if {@code capacity < 1} */ public ArrayBlockingQueue(int capacity, boolean fair) { if (capacity <= 0) throw new IllegalArgumentException(); - this.items = (E[]) new Object[capacity]; + this.items = new Object[capacity]; lock = new ReentrantLock(fair); notEmpty = lock.newCondition(); notFull = lock.newCondition(); } /** - * Creates an ArrayBlockingQueue with the given (fixed) + * Creates an {@code ArrayBlockingQueue} with the given (fixed) * capacity, the specified access policy and initially containing the * elements of the given collection, * added in traversal order of the collection's iterator. * * @param capacity the capacity of this queue - * @param fair if true then queue accesses for threads blocked + * @param fair if {@code true} then queue accesses for threads blocked * on insertion or removal, are processed in FIFO order; - * if false the access order is unspecified. + * if {@code false} the access order is unspecified. * @param c the collection of elements to initially contain - * @throws IllegalArgumentException if capacity is less than - * c.size(), or less than 1. + * @throws IllegalArgumentException if {@code capacity} is less than + * {@code c.size()}, or less than 1. * @throws NullPointerException if the specified collection or any * of its elements are null */ public ArrayBlockingQueue(int capacity, boolean fair, Collection c) { this(capacity, fair); - if (capacity < c.size()) - throw new IllegalArgumentException(); - for (E e : c) - add(e); + final ReentrantLock lock = this.lock; + lock.lock(); // Lock only for visibility, not mutual exclusion + try { + int i = 0; + try { + for (E e : c) { + checkNotNull(e); + items[i++] = e; + } + } catch (ArrayIndexOutOfBoundsException ex) { + throw new IllegalArgumentException(); + } + count = i; + putIndex = (i == capacity) ? 0 : i; + } finally { + lock.unlock(); + } } /** * 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 true upon success and throwing an - * IllegalStateException if this queue is full. + * returning {@code true} upon success and throwing an + * {@code IllegalStateException} if this queue is full. * * @param e the element to add - * @return true (as specified by {@link Collection#add}) + * @return {@code true} (as specified by {@link Collection#add}) * @throws IllegalStateException if this queue is full * @throws NullPointerException if the specified element is null */ @@ -240,14 +286,14 @@ public class ArrayBlockingQueue extends AbstractQueue /** * 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 true upon success and false if this queue + * returning {@code true} upon success and {@code false} if this queue * is full. This method is generally preferable to method {@link #add}, * which can fail to insert an element only by throwing an exception. * * @throws NullPointerException if the specified element is null */ public boolean offer(E e) { - if (e == null) throw new NullPointerException(); + checkNotNull(e); final ReentrantLock lock = this.lock; lock.lock(); try { @@ -270,18 +316,12 @@ public class ArrayBlockingQueue extends AbstractQueue * @throws NullPointerException {@inheritDoc} */ public void put(E e) throws InterruptedException { - if (e == null) throw new NullPointerException(); - final E[] items = this.items; + checkNotNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { - try { - while (count == items.length) - notFull.await(); - } catch (InterruptedException ie) { - notFull.signal(); // propagate to non-interrupted thread - throw ie; - } + while (count == items.length) + notFull.await(); insert(e); } finally { lock.unlock(); @@ -299,25 +339,18 @@ public class ArrayBlockingQueue extends AbstractQueue public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { - if (e == null) throw new NullPointerException(); + checkNotNull(e); long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { - for (;;) { - if (count != items.length) { - insert(e); - return true; - } + while (count == items.length) { if (nanos <= 0) return false; - try { - nanos = notFull.awaitNanos(nanos); - } catch (InterruptedException ie) { - notFull.signal(); // propagate to non-interrupted thread - throw ie; - } + nanos = notFull.awaitNanos(nanos); } + insert(e); + return true; } finally { lock.unlock(); } @@ -327,10 +360,7 @@ public class ArrayBlockingQueue extends AbstractQueue final ReentrantLock lock = this.lock; lock.lock(); try { - if (count == 0) - return null; - E x = extract(); - return x; + return (count == 0) ? null : extract(); } finally { lock.unlock(); } @@ -340,15 +370,9 @@ public class ArrayBlockingQueue extends AbstractQueue final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { - try { - while (count == 0) - notEmpty.await(); - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to non-interrupted thread - throw ie; - } - E x = extract(); - return x; + while (count == 0) + notEmpty.await(); + return extract(); } finally { lock.unlock(); } @@ -359,21 +383,12 @@ public class ArrayBlockingQueue extends AbstractQueue final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { - for (;;) { - if (count != 0) { - E x = extract(); - return x; - } + while (count == 0) { if (nanos <= 0) return null; - try { - nanos = notEmpty.awaitNanos(nanos); - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to non-interrupted thread - throw ie; - } - + nanos = notEmpty.awaitNanos(nanos); } + return extract(); } finally { lock.unlock(); } @@ -383,7 +398,7 @@ public class ArrayBlockingQueue extends AbstractQueue final ReentrantLock lock = this.lock; lock.lock(); try { - return (count == 0) ? null : items[takeIndex]; + return (count == 0) ? null : itemAt(takeIndex); } finally { lock.unlock(); } @@ -412,10 +427,10 @@ public class ArrayBlockingQueue extends AbstractQueue * 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 size of this queue. + * less the current {@code size} of this queue. * *

Note that you cannot always tell if an attempt to insert - * an element will succeed by inspecting remainingCapacity + * 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. */ @@ -431,59 +446,56 @@ public class ArrayBlockingQueue extends AbstractQueue /** * Removes a single instance of the specified element from this queue, - * if it is present. More formally, removes an element e such - * that o.equals(e), 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 true 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). * + *

Removal of interior elements in circular array based queues + * is an intrinsically slow and disruptive operation, so should + * be undertaken only in exceptional circumstances, ideally + * only when the queue is known not to be accessible by other + * threads. + * * @param o element to be removed from this queue, if present - * @return true 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; - final E[] items = this.items; + final Object[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { - int i = takeIndex; - int k = 0; - for (;;) { - if (k++ >= count) - return false; + for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) { if (o.equals(items[i])) { removeAt(i); return true; } - i = inc(i); } - + return false; } finally { lock.unlock(); } } /** - * Returns true if this queue contains the specified element. - * More formally, returns true if and only if this queue contains - * at least one element e such that o.equals(e). + * Returns {@code true} if this queue contains the specified element. + * More formally, returns {@code true} if and only if this queue contains + * at least one element {@code e} such that {@code o.equals(e)}. * * @param o object to be checked for containment in this queue - * @return true if this queue contains the specified element + * @return {@code true} if this queue contains the specified element */ public boolean contains(Object o) { if (o == null) return false; - final E[] items = this.items; + final Object[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { - int i = takeIndex; - int k = 0; - while (k++ < count) { + for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) if (o.equals(items[i])) return true; - i = inc(i); - } return false; } finally { lock.unlock(); @@ -504,17 +516,14 @@ public class ArrayBlockingQueue extends AbstractQueue * @return an array containing all of the elements in this queue */ public Object[] toArray() { - final E[] items = this.items; + final Object[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { + final int count = this.count; Object[] a = new Object[count]; - int k = 0; - int i = takeIndex; - while (k < count) { - a[k++] = items[i]; - i = inc(i); - } + for (int i = takeIndex, k = 0; k < count; i = inc(i), k++) + a[k] = items[i]; return a; } finally { lock.unlock(); @@ -531,22 +540,22 @@ public class ArrayBlockingQueue extends AbstractQueue *

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 - * null. + * {@code null}. * *

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. * - *

Suppose x is a queue known to contain only strings. + *

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 String: + * allocated array of {@code String}: * *

      *     String[] y = x.toArray(new String[0]);
* - * Note that toArray(new Object[0]) is identical in function to - * toArray(). + * 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 @@ -557,24 +566,20 @@ public class ArrayBlockingQueue extends AbstractQueue * this queue * @throws NullPointerException if the specified array is null */ + @SuppressWarnings("unchecked") public T[] toArray(T[] a) { - final E[] items = this.items; + final Object[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { - if (a.length < count) + final int count = this.count; + final int len = a.length; + if (len < count) a = (T[])java.lang.reflect.Array.newInstance( - a.getClass().getComponentType(), - count - ); - - int k = 0; - int i = takeIndex; - while (k < count) { - a[k++] = (T)items[i]; - i = inc(i); - } - if (a.length > count) + a.getClass().getComponentType(), count); + for (int i = takeIndex, k = 0; k < count; i = inc(i), k++) + a[k] = (T) items[i]; + if (len > count) a[count] = null; return a; } finally { @@ -586,7 +591,19 @@ public class ArrayBlockingQueue extends AbstractQueue final ReentrantLock lock = this.lock; lock.lock(); try { - return super.toString(); + int k = count; + if (k == 0) + return "[]"; + + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (int i = takeIndex; ; i = inc(i)) { + Object e = items[i]; + sb.append(e == this ? "(this Collection)" : e); + if (--k == 0) + return sb.append(']').toString(); + sb.append(',').append(' '); + } } finally { lock.unlock(); } @@ -597,16 +614,12 @@ public class ArrayBlockingQueue extends AbstractQueue * The queue will be empty after this call returns. */ public void clear() { - final E[] items = this.items; + final Object[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { - int i = takeIndex; - int k = count; - while (k-- > 0) { + for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) items[i] = null; - i = inc(i); - } count = 0; putIndex = 0; takeIndex = 0; @@ -623,11 +636,10 @@ public class ArrayBlockingQueue extends AbstractQueue * @throws IllegalArgumentException {@inheritDoc} */ public int drainTo(Collection c) { - if (c == null) - throw new NullPointerException(); + checkNotNull(c); if (c == this) throw new IllegalArgumentException(); - final E[] items = this.items; + final Object[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { @@ -635,7 +647,7 @@ public class ArrayBlockingQueue extends AbstractQueue int n = 0; int max = count; while (n < max) { - c.add(items[i]); + c.add(this.cast(items[i])); items[i] = null; i = inc(i); ++n; @@ -659,22 +671,20 @@ public class ArrayBlockingQueue extends AbstractQueue * @throws IllegalArgumentException {@inheritDoc} */ public int drainTo(Collection c, int maxElements) { - if (c == null) - throw new NullPointerException(); + checkNotNull(c); if (c == this) throw new IllegalArgumentException(); if (maxElements <= 0) return 0; - final E[] items = this.items; + final Object[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { int i = takeIndex; int n = 0; - int sz = count; - int max = (maxElements < count)? maxElements : count; + int max = (maxElements < count) ? maxElements : count; while (n < max) { - c.add(items[i]); + c.add(this.cast(items[i])); items[i] = null; i = inc(i); ++n; @@ -690,11 +700,13 @@ public class ArrayBlockingQueue extends AbstractQueue } } - /** * 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}, + * 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. @@ -702,83 +714,65 @@ public class ArrayBlockingQueue extends AbstractQueue * @return an iterator over the elements in this queue in proper sequence */ public Iterator iterator() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - return new Itr(); - } finally { - lock.unlock(); - } + return new Itr(); } /** - * Iterator for ArrayBlockingQueue + * Iterator for ArrayBlockingQueue. To maintain weak consistency + * with respect to puts and takes, we (1) read ahead one slot, so + * as to not report hasNext true but then not have an element to + * return -- however we later recheck this slot to use the most + * current value; (2) ensure that each array slot is traversed at + * most once (by tracking "remaining" elements); (3) skip over + * null slots, which can occur if takes race ahead of iterators. + * However, for circular array-based queues, we cannot rely on any + * well established definition of what it means to be weakly + * consistent with respect to interior removes since these may + * require slot overwrites in the process of sliding elements to + * cover gaps. So we settle for resiliency, operating on + * established apparent nexts, which may miss some elements that + * have moved between calls to next. */ private class Itr implements Iterator { - /** - * Index of element to be returned by next, - * or a negative number if no such. - */ - private int nextIndex; - - /** - * 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; - - /** - * Index of element returned by most recent call to next. - * Reset to -1 if this element is deleted by a call to remove. - */ - private int lastRet; + private int remaining; // Number of elements yet to be returned + private int nextIndex; // Index of element to be returned by next + private E nextItem; // Element to be returned by next call to next + private E lastItem; // Element returned by last call to next + private int lastRet; // Index of last element returned, or -1 if none Itr() { - lastRet = -1; - if (count == 0) - nextIndex = -1; - else { - nextIndex = takeIndex; - nextItem = items[takeIndex]; + final ReentrantLock lock = ArrayBlockingQueue.this.lock; + lock.lock(); + try { + lastRet = -1; + if ((remaining = count) > 0) + nextItem = itemAt(nextIndex = takeIndex); + } finally { + lock.unlock(); } } public boolean hasNext() { - /* - * No sync. We can return true by mistake here - * only if this iterator passed across threads, - * which we don't support anyway. - */ - return nextIndex >= 0; - } - - /** - * Checks whether nextIndex is valid; if so setting nextItem. - * Stops iterator when either hits putIndex or sees null item. - */ - private void checkNext() { - if (nextIndex == putIndex) { - nextIndex = -1; - nextItem = null; - } else { - nextItem = items[nextIndex]; - if (nextItem == null) - nextIndex = -1; - } + return remaining > 0; } public E next() { final ReentrantLock lock = ArrayBlockingQueue.this.lock; lock.lock(); try { - if (nextIndex < 0) + if (remaining <= 0) throw new NoSuchElementException(); lastRet = nextIndex; - E x = nextItem; - nextIndex = inc(nextIndex); - checkNext(); + E x = itemAt(nextIndex); // check for fresher value + if (x == null) { + x = nextItem; // we are forced to report old value + lastItem = null; // but ensure remove fails + } + else + lastItem = x; + while (--remaining > 0 && // skip over nulls + (nextItem = itemAt(nextIndex = inc(nextIndex))) == null) + ; return x; } finally { lock.unlock(); @@ -793,15 +787,19 @@ public class ArrayBlockingQueue extends AbstractQueue if (i == -1) throw new IllegalStateException(); lastRet = -1; - - int ti = takeIndex; - removeAt(i); - // back up cursor (reset to front if was first element) - nextIndex = (i == ti) ? takeIndex : i; - checkNext(); + E x = lastItem; + lastItem = null; + // only remove if item still at index + if (x != null && x == items[i]) { + boolean removingHead = (i == takeIndex); + removeAt(i); + if (!removingHead) + nextIndex = dec(nextIndex); + } } finally { lock.unlock(); } } } + } diff --git a/jdk/src/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java b/jdk/src/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java index 72133fedad7..2158084f9f9 100644 --- a/jdk/src/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java +++ b/jdk/src/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java @@ -869,6 +869,8 @@ public class ConcurrentLinkedDeque /** * Inserts the specified element at the front of this deque. + * As the deque is unbounded, this method will never throw + * {@link IllegalStateException}. * * @throws NullPointerException if the specified element is null */ @@ -878,6 +880,8 @@ public class ConcurrentLinkedDeque /** * Inserts the specified element at the end of this deque. + * As the deque is unbounded, this method will never throw + * {@link IllegalStateException}. * *

This method is equivalent to {@link #add}. * @@ -889,8 +893,9 @@ public class ConcurrentLinkedDeque /** * Inserts the specified element at the front of this deque. + * As the deque is unbounded, this method will never return {@code false}. * - * @return {@code true} always + * @return {@code true} (as specified by {@link Deque#offerFirst}) * @throws NullPointerException if the specified element is null */ public boolean offerFirst(E e) { @@ -900,10 +905,11 @@ public class ConcurrentLinkedDeque /** * Inserts the specified element at the end of this deque. + * As the deque is unbounded, this method will never return {@code false}. * *

This method is equivalent to {@link #add}. * - * @return {@code true} always + * @return {@code true} (as specified by {@link Deque#offerLast}) * @throws NullPointerException if the specified element is null */ public boolean offerLast(E e) { @@ -983,6 +989,7 @@ public class ConcurrentLinkedDeque /** * Inserts the specified element at the tail of this deque. + * As the deque is unbounded, this method will never return {@code false}. * * @return {@code true} (as specified by {@link Queue#offer}) * @throws NullPointerException if the specified element is null @@ -993,6 +1000,8 @@ public class ConcurrentLinkedDeque /** * Inserts the specified element at the tail of this deque. + * As the deque is unbounded, this method will never throw + * {@link IllegalStateException} or return {@code false}. * * @return {@code true} (as specified by {@link Collection#add}) * @throws NullPointerException if the specified element is null diff --git a/jdk/src/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java b/jdk/src/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java index 6ff1b8a5119..b7beda274da 100644 --- a/jdk/src/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java +++ b/jdk/src/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java @@ -269,6 +269,8 @@ public class ConcurrentLinkedQueue extends AbstractQueue /** * Inserts the specified element at the tail of this queue. + * As the queue is unbounded, this method will never throw + * {@link IllegalStateException} or return {@code false}. * * @return {@code true} (as specified by {@link Collection#add}) * @throws NullPointerException if the specified element is null @@ -298,6 +300,7 @@ public class ConcurrentLinkedQueue extends AbstractQueue /** * Inserts the specified element at the tail of this queue. + * As the queue is unbounded, this method will never return {@code false}. * * @return {@code true} (as specified by {@link Queue#offer}) * @throws NullPointerException if the specified element is null diff --git a/jdk/src/share/classes/java/util/concurrent/ConcurrentSkipListMap.java b/jdk/src/share/classes/java/util/concurrent/ConcurrentSkipListMap.java index 4e0457f2877..64d28cf6040 100644 --- a/jdk/src/share/classes/java/util/concurrent/ConcurrentSkipListMap.java +++ b/jdk/src/share/classes/java/util/concurrent/ConcurrentSkipListMap.java @@ -374,17 +374,11 @@ public class ConcurrentSkipListMap extends AbstractMap null, null, 1); } - /** Updater for casHead */ - private static final - AtomicReferenceFieldUpdater - headUpdater = AtomicReferenceFieldUpdater.newUpdater - (ConcurrentSkipListMap.class, HeadIndex.class, "head"); - /** * compareAndSet head node */ private boolean casHead(HeadIndex cmp, HeadIndex val) { - return headUpdater.compareAndSet(this, cmp, val); + return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); } /* ---------------- Nodes -------------- */ @@ -423,28 +417,18 @@ public class ConcurrentSkipListMap extends AbstractMap this.next = next; } - /** Updater for casNext */ - static final AtomicReferenceFieldUpdater - nextUpdater = AtomicReferenceFieldUpdater.newUpdater - (Node.class, Node.class, "next"); - - /** Updater for casValue */ - static final AtomicReferenceFieldUpdater - valueUpdater = AtomicReferenceFieldUpdater.newUpdater - (Node.class, Object.class, "value"); - /** * compareAndSet value field */ boolean casValue(Object cmp, Object val) { - return valueUpdater.compareAndSet(this, cmp, val); + return UNSAFE.compareAndSwapObject(this, valueOffset, cmp, val); } /** * compareAndSet next field */ boolean casNext(Node cmp, Node val) { - return nextUpdater.compareAndSet(this, cmp, val); + return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); } /** @@ -522,6 +506,14 @@ public class ConcurrentSkipListMap extends AbstractMap return null; return new AbstractMap.SimpleImmutableEntry(key, v); } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); + private static final long valueOffset = + objectFieldOffset(UNSAFE, "value", Node.class); + private static final long nextOffset = + objectFieldOffset(UNSAFE, "next", Node.class); + } /* ---------------- Indexing -------------- */ @@ -547,16 +539,11 @@ public class ConcurrentSkipListMap extends AbstractMap this.right = right; } - /** Updater for casRight */ - static final AtomicReferenceFieldUpdater - rightUpdater = AtomicReferenceFieldUpdater.newUpdater - (Index.class, Index.class, "right"); - /** * compareAndSet right field */ final boolean casRight(Index cmp, Index val) { - return rightUpdater.compareAndSet(this, cmp, val); + return UNSAFE.compareAndSwapObject(this, rightOffset, cmp, val); } /** @@ -591,6 +578,12 @@ public class ConcurrentSkipListMap extends AbstractMap final boolean unlink(Index succ) { return !indexesDeletedNode() && casRight(succ, succ.right); } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); + private static final long rightOffset = + objectFieldOffset(UNSAFE, "right", Index.class); + } /* ---------------- Head nodes -------------- */ @@ -640,7 +633,8 @@ public class ConcurrentSkipListMap extends AbstractMap * cast key as Comparable, which may cause ClassCastException, * which is propagated back to caller. */ - private Comparable comparable(Object key) throws ClassCastException { + private Comparable comparable(Object key) + throws ClassCastException { if (key == null) throw new NullPointerException(); if (comparator != null) @@ -799,68 +793,12 @@ public class ConcurrentSkipListMap extends AbstractMap } /** - * 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. + * Gets value for key using findNode. * @param okey the key * @return the value, or null if absent */ private V doGet(Object okey) { Comparable key = comparable(okey); - Node bound = null; - Index q = head; - Index r = q.right; - Node n; - K k; - int c; - for (;;) { - Index 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 key) { /* * Loop needed here and elsewhere in case value field goes * null just as it is about to be returned, in which case we @@ -943,7 +881,7 @@ public class ConcurrentSkipListMap extends AbstractMap x ^= x << 13; x ^= x >>> 17; randomSeed = x ^= x << 5; - if ((x & 0x8001) != 0) // test highest and lowest bits + if ((x & 0x80000001) != 0) // test highest and lowest bits return 0; int level = 1; while (((x >>>= 1) & 1) != 0) ++level; @@ -1256,7 +1194,7 @@ public class ConcurrentSkipListMap extends AbstractMap Node n = b.next; for (;;) { if (n == null) - return (b.isBaseHeader())? null : b; + return b.isBaseHeader() ? null : b; Node f = n.next; // inconsistent read if (n != b.next) break; @@ -1374,7 +1312,7 @@ public class ConcurrentSkipListMap extends AbstractMap Node n = b.next; for (;;) { if (n == null) - return ((rel & LT) == 0 || b.isBaseHeader())? null : b; + return ((rel & LT) == 0 || b.isBaseHeader()) ? null : b; Node f = n.next; if (n != b.next) // inconsistent read break; @@ -1390,7 +1328,7 @@ public class ConcurrentSkipListMap extends AbstractMap (c < 0 && (rel & LT) == 0)) return n; if ( c <= 0 && (rel & LT) != 0) - return (b.isBaseHeader())? null : b; + return b.isBaseHeader() ? null : b; b = n; n = f; } @@ -1744,7 +1682,7 @@ public class ConcurrentSkipListMap extends AbstractMap if (n.getValidValue() != null) ++count; } - return (count >= Integer.MAX_VALUE)? Integer.MAX_VALUE : (int)count; + return (count >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) count; } /** @@ -2099,7 +2037,7 @@ public class ConcurrentSkipListMap extends AbstractMap */ public K lowerKey(K key) { Node n = findNear(key, LT); - return (n == null)? null : n.key; + return (n == null) ? null : n.key; } /** @@ -2123,7 +2061,7 @@ public class ConcurrentSkipListMap extends AbstractMap */ public K floorKey(K key) { Node n = findNear(key, LT|EQ); - return (n == null)? null : n.key; + return (n == null) ? null : n.key; } /** @@ -2145,7 +2083,7 @@ public class ConcurrentSkipListMap extends AbstractMap */ public K ceilingKey(K key) { Node n = findNear(key, GT|EQ); - return (n == null)? null : n.key; + return (n == null) ? null : n.key; } /** @@ -2169,7 +2107,7 @@ public class ConcurrentSkipListMap extends AbstractMap */ public K higherKey(K key) { Node n = findNear(key, GT); - return (n == null)? null : n.key; + return (n == null) ? null : n.key; } /** @@ -2342,7 +2280,8 @@ public class ConcurrentSkipListMap extends AbstractMap return list; } - static final class KeySet extends AbstractSet implements NavigableSet { + static final class KeySet + extends AbstractSet implements NavigableSet { private final ConcurrentNavigableMap m; KeySet(ConcurrentNavigableMap map) { m = map; } public int size() { return m.size(); } @@ -2359,11 +2298,11 @@ public class ConcurrentSkipListMap extends AbstractMap public E last() { return m.lastKey(); } public E pollFirst() { Map.Entry e = m.pollFirstEntry(); - return e == null? null : e.getKey(); + return (e == null) ? null : e.getKey(); } public E pollLast() { Map.Entry e = m.pollLastEntry(); - return e == null? null : e.getKey(); + return (e == null) ? null : e.getKey(); } public Iterator iterator() { if (m instanceof ConcurrentSkipListMap) @@ -2710,9 +2649,9 @@ public class ConcurrentSkipListMap extends AbstractMap rel &= ~m.LT; } if (tooLow(key)) - return ((rel & m.LT) != 0)? null : lowestEntry(); + return ((rel & m.LT) != 0) ? null : lowestEntry(); if (tooHigh(key)) - return ((rel & m.LT) != 0)? highestEntry() : null; + return ((rel & m.LT) != 0) ? highestEntry() : null; for (;;) { Node n = m.findNear(key, rel); if (n == null || !inBounds(n.key)) @@ -2783,7 +2722,7 @@ public class ConcurrentSkipListMap extends AbstractMap public V remove(Object key) { K k = (K)key; - return (!inBounds(k))? null : m.remove(k); + return (!inBounds(k)) ? null : m.remove(k); } public int size() { @@ -2794,7 +2733,7 @@ public class ConcurrentSkipListMap extends AbstractMap if (n.getValidValue() != null) ++count; } - return count >= Integer.MAX_VALUE? Integer.MAX_VALUE : (int)count; + return count >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)count; } public boolean isEmpty() { @@ -2972,27 +2911,27 @@ public class ConcurrentSkipListMap extends AbstractMap } public K firstKey() { - return isDescending? highestKey() : lowestKey(); + return isDescending ? highestKey() : lowestKey(); } public K lastKey() { - return isDescending? lowestKey() : highestKey(); + return isDescending ? lowestKey() : highestKey(); } public Map.Entry firstEntry() { - return isDescending? highestEntry() : lowestEntry(); + return isDescending ? highestEntry() : lowestEntry(); } public Map.Entry lastEntry() { - return isDescending? lowestEntry() : highestEntry(); + return isDescending ? lowestEntry() : highestEntry(); } public Map.Entry pollFirstEntry() { - return isDescending? removeHighest() : removeLowest(); + return isDescending ? removeHighest() : removeLowest(); } public Map.Entry pollLastEntry() { - return isDescending? removeLowest() : removeHighest(); + return isDescending ? removeLowest() : removeHighest(); } /* ---------------- Submap Views -------------- */ @@ -3141,4 +3080,22 @@ public class ConcurrentSkipListMap extends AbstractMap } } } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); + private static final long headOffset = + objectFieldOffset(UNSAFE, "head", ConcurrentSkipListMap.class); + + 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/jdk/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java b/jdk/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java index 465426ff823..8c5193538fa 100644 --- a/jdk/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java +++ b/jdk/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java @@ -832,7 +832,7 @@ public class CopyOnWriteArrayList } /** - * Save the state of the list to a stream (i.e., serialize it). + * Saves the state of the list to a stream (that is, serializes it). * * @serialData The length of the array backing the list is emitted * (int), followed by all of its elements (each an Object) @@ -842,27 +842,25 @@ public class CopyOnWriteArrayList private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ - // Write out element count, and any hidden stuff s.defaultWriteObject(); Object[] elements = getArray(); - int len = elements.length; // Write out array length - s.writeInt(len); + s.writeInt(elements.length); // Write out all elements in the proper order. - for (int i = 0; i < len; i++) - s.writeObject(elements[i]); + for (Object element : elements) + s.writeObject(element); } /** - * Reconstitute the list from a stream (i.e., deserialize it). + * Reconstitutes the list from a stream (that is, deserializes it). + * * @param s the stream */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { - // Read in size, and any hidden stuff s.defaultReadObject(); // bind to new lock diff --git a/jdk/src/share/classes/java/util/concurrent/ForkJoinPool.java b/jdk/src/share/classes/java/util/concurrent/ForkJoinPool.java index 263552a93ea..22938fe4ba9 100644 --- a/jdk/src/share/classes/java/util/concurrent/ForkJoinPool.java +++ b/jdk/src/share/classes/java/util/concurrent/ForkJoinPool.java @@ -525,8 +525,8 @@ public class ForkJoinPool extends AbstractExecutorService { */ private volatile long eventWaiters; - private static final int EVENT_COUNT_SHIFT = 32; - private static final long WAITER_ID_MASK = (1L << 16) - 1L; + private static final int EVENT_COUNT_SHIFT = 32; + private static final int WAITER_ID_MASK = (1 << 16) - 1; /** * A counter for events that may wake up worker threads: @@ -615,7 +615,7 @@ public class ForkJoinPool extends AbstractExecutorService { // are usually manually inlined by callers /** - * Increments running count part of workerCounts + * Increments running count part of workerCounts. */ final void incrementRunningCount() { int c; @@ -625,7 +625,17 @@ public class ForkJoinPool extends AbstractExecutorService { } /** - * Tries to decrement running count unless already zero + * Tries to increment running count part of workerCounts. + */ + final boolean tryIncrementRunningCount() { + int c; + return UNSAFE.compareAndSwapInt(this, workerCountsOffset, + c = workerCounts, + c + ONE_RUNNING); + } + + /** + * Tries to decrement running count unless already zero. */ final boolean tryDecrementRunningCount() { int wc = workerCounts; @@ -698,10 +708,11 @@ public class ForkJoinPool extends AbstractExecutorService { for (k = 0; k < n && ws[k] != null; ++k) ; if (k == n) - ws = Arrays.copyOf(ws, n << 1); + ws = workers = Arrays.copyOf(ws, n << 1); } ws[k] = w; - workers = ws; // volatile array write ensures slot visibility + int c = eventCount; // advance event count to ensure visibility + UNSAFE.compareAndSwapInt(this, eventCountOffset, c, c+1); } finally { lock.unlock(); } @@ -734,7 +745,7 @@ public class ForkJoinPool extends AbstractExecutorService { */ final void workerTerminated(ForkJoinWorkerThread w) { forgetWorker(w); - decrementWorkerCounts(w.isTrimmed()? 0 : ONE_RUNNING, ONE_TOTAL); + decrementWorkerCounts(w.isTrimmed() ? 0 : ONE_RUNNING, ONE_TOTAL); while (w.stealCount != 0) // collect final count tryAccumulateStealCount(w); tryTerminate(false); @@ -746,24 +757,23 @@ public class ForkJoinPool extends AbstractExecutorService { * Releases workers blocked on a count not equal to current count. * Normally called after precheck that eventWaiters isn't zero to * avoid wasted array checks. Gives up upon a change in count or - * upon releasing two workers, letting others take over. + * upon releasing four workers, letting others take over. */ private void releaseEventWaiters() { ForkJoinWorkerThread[] ws = workers; int n = ws.length; long h = eventWaiters; int ec = eventCount; - boolean releasedOne = false; + int releases = 4; ForkJoinWorkerThread w; int id; - while ((id = ((int)(h & WAITER_ID_MASK)) - 1) >= 0 && + while ((id = (((int)h) & WAITER_ID_MASK) - 1) >= 0 && (int)(h >>> EVENT_COUNT_SHIFT) != ec && id < n && (w = ws[id]) != null) { if (UNSAFE.compareAndSwapLong(this, eventWaitersOffset, h, w.nextWaiter)) { LockSupport.unpark(w); - if (releasedOne) // exit on second release + if (--releases == 0) break; - releasedOne = true; } if (eventCount != ec) break; @@ -793,7 +803,7 @@ public class ForkJoinPool extends AbstractExecutorService { long nh = (((long)ec) << EVENT_COUNT_SHIFT) | ((long)(w.poolIndex+1)); long h; while ((runState < SHUTDOWN || !tryTerminate(false)) && - (((int)((h = eventWaiters) & WAITER_ID_MASK)) == 0 || + (((int)(h = eventWaiters) & WAITER_ID_MASK) == 0 || (int)(h >>> EVENT_COUNT_SHIFT) == ec) && eventCount == ec) { if (UNSAFE.compareAndSwapLong(this, eventWaitersOffset, @@ -820,9 +830,9 @@ public class ForkJoinPool extends AbstractExecutorService { if (tryAccumulateStealCount(w)) { // transfer while idle boolean untimed = (w.nextWaiter != 0L || (workerCounts & RUNNING_COUNT_MASK) <= 1); - long startTime = untimed? 0 : System.nanoTime(); + long startTime = untimed ? 0 : System.nanoTime(); Thread.interrupted(); // clear/ignore interrupt - if (eventCount != ec || w.isTerminating()) + if (w.isTerminating() || eventCount != ec) break; // recheck after clear if (untimed) LockSupport.park(w); @@ -860,7 +870,8 @@ public class ForkJoinPool extends AbstractExecutorService { if ((sw = spareWaiters) != 0 && (id = (sw & SPARE_ID_MASK) - 1) >= 0 && id < n && (w = ws[id]) != null && - (workerCounts & RUNNING_COUNT_MASK) < parallelism && + (runState >= TERMINATING || + (workerCounts & RUNNING_COUNT_MASK) < parallelism) && spareWaiters == sw && UNSAFE.compareAndSwapInt(this, spareWaitersOffset, sw, w.nextSpare)) { @@ -914,12 +925,8 @@ public class ForkJoinPool extends AbstractExecutorService { break; } w.start(recordWorker(w), ueh); - if ((workerCounts >>> TOTAL_COUNT_SHIFT) >= pc) { - int c; // advance event count - UNSAFE.compareAndSwapInt(this, eventCountOffset, - c = eventCount, c+1); + if ((workerCounts >>> TOTAL_COUNT_SHIFT) >= pc) break; // add at most one unless total below target - } } } if (eventWaiters != 0L) @@ -955,7 +962,7 @@ public class ForkJoinPool extends AbstractExecutorService { } else if ((h = eventWaiters) != 0L) { long nh; - int id = ((int)(h & WAITER_ID_MASK)) - 1; + int id = (((int)h) & WAITER_ID_MASK) - 1; if (id >= 0 && id < n && (w = ws[id]) != null && (nh = w.nextWaiter) != 0L && // keep at least one worker UNSAFE.compareAndSwapLong(this, eventWaitersOffset, h, nh)) @@ -1003,24 +1010,31 @@ public class ForkJoinPool extends AbstractExecutorService { int pc = parallelism; while (w.runState == 0) { int rs = runState; - if (rs >= TERMINATING) { // propagate shutdown + if (rs >= TERMINATING) { // propagate shutdown w.shutdown(); break; } if ((inactivate || (active && (rs & ACTIVE_COUNT_MASK) >= pc)) && - UNSAFE.compareAndSwapInt(this, runStateOffset, rs, rs - 1)) + UNSAFE.compareAndSwapInt(this, runStateOffset, rs, --rs)) { inactivate = active = w.active = false; - int wc = workerCounts; + if (rs == SHUTDOWN) { // all inactive and shut down + tryTerminate(false); + continue; + } + } + int wc = workerCounts; // try to suspend as spare if ((wc & RUNNING_COUNT_MASK) > pc) { if (!(inactivate |= active) && // must inactivate to suspend - workerCounts == wc && // try to suspend as spare + workerCounts == wc && UNSAFE.compareAndSwapInt(this, workerCountsOffset, wc, wc - ONE_RUNNING)) w.suspendAsSpare(); } else if ((wc >>> TOTAL_COUNT_SHIFT) < pc) helpMaintainParallelism(); // not enough workers - else if (!ran) { + else if (ran) + break; + else { long h = eventWaiters; int ec = eventCount; if (h != 0L && (int)(h >>> EVENT_COUNT_SHIFT) != ec) @@ -1032,8 +1046,6 @@ public class ForkJoinPool extends AbstractExecutorService { else if (!(inactivate |= active)) eventSync(w, wec); // must inactivate before sync } - else - break; } } @@ -1043,35 +1055,67 @@ public class ForkJoinPool extends AbstractExecutorService { * * @param joinMe the task to join * @param worker the current worker thread + * @param timed true if wait should time out + * @param nanos timeout value if timed */ - final void awaitJoin(ForkJoinTask joinMe, ForkJoinWorkerThread worker) { + final void awaitJoin(ForkJoinTask joinMe, ForkJoinWorkerThread worker, + boolean timed, long nanos) { + long startTime = timed ? System.nanoTime() : 0L; int retries = 2 + (parallelism >> 2); // #helpJoins before blocking + boolean running = true; // false when count decremented while (joinMe.status >= 0) { - int wc; - worker.helpJoinTask(joinMe); + if (runState >= TERMINATING) { + joinMe.cancelIgnoringExceptions(); + break; + } + running = worker.helpJoinTask(joinMe, running); if (joinMe.status < 0) break; - else if (retries > 0) + if (retries > 0) { --retries; - else if (((wc = workerCounts) & RUNNING_COUNT_MASK) != 0 && - UNSAFE.compareAndSwapInt(this, workerCountsOffset, - wc, wc - ONE_RUNNING)) { - int stat, c; long h; - while ((stat = joinMe.status) >= 0 && - (h = eventWaiters) != 0L && // help release others - (int)(h >>> EVENT_COUNT_SHIFT) != eventCount) - releaseEventWaiters(); - if (stat >= 0 && - ((workerCounts & RUNNING_COUNT_MASK) == 0 || - (stat = - joinMe.internalAwaitDone(JOIN_TIMEOUT_MILLIS)) >= 0)) - helpMaintainParallelism(); // timeout or no running workers - do {} while (!UNSAFE.compareAndSwapInt - (this, workerCountsOffset, - c = workerCounts, c + ONE_RUNNING)); - if (stat < 0) - break; // else restart + continue; } + int wc = workerCounts; + if ((wc & RUNNING_COUNT_MASK) != 0) { + if (running) { + if (!UNSAFE.compareAndSwapInt(this, workerCountsOffset, + wc, wc - ONE_RUNNING)) + continue; + running = false; + } + long h = eventWaiters; + if (h != 0L && (int)(h >>> EVENT_COUNT_SHIFT) != eventCount) + releaseEventWaiters(); + if ((workerCounts & RUNNING_COUNT_MASK) != 0) { + long ms; int ns; + if (!timed) { + ms = JOIN_TIMEOUT_MILLIS; + ns = 0; + } + else { // at most JOIN_TIMEOUT_MILLIS per wait + long nt = nanos - (System.nanoTime() - startTime); + if (nt <= 0L) + break; + ms = nt / 1000000; + if (ms > JOIN_TIMEOUT_MILLIS) { + ms = JOIN_TIMEOUT_MILLIS; + ns = 0; + } + else + ns = (int) (nt % 1000000); + } + joinMe.internalAwaitDone(ms, ns); + } + if (joinMe.status < 0) + break; + } + helpMaintainParallelism(); + } + if (!running) { + int c; + do {} while (!UNSAFE.compareAndSwapInt + (this, workerCountsOffset, + c = workerCounts, c + ONE_RUNNING)); } } @@ -1082,9 +1126,10 @@ public class ForkJoinPool extends AbstractExecutorService { throws InterruptedException { while (!blocker.isReleasable()) { int wc = workerCounts; - if ((wc & RUNNING_COUNT_MASK) != 0 && - UNSAFE.compareAndSwapInt(this, workerCountsOffset, - wc, wc - ONE_RUNNING)) { + if ((wc & RUNNING_COUNT_MASK) == 0) + helpMaintainParallelism(); + else if (UNSAFE.compareAndSwapInt(this, workerCountsOffset, + wc, wc - ONE_RUNNING)) { try { while (!blocker.isReleasable()) { long h = eventWaiters; @@ -1129,12 +1174,11 @@ public class ForkJoinPool extends AbstractExecutorService { // Finish now if all threads terminated; else in some subsequent call if ((workerCounts >>> TOTAL_COUNT_SHIFT) == 0) { advanceRunLevel(TERMINATED); - termination.arrive(); + termination.forceTermination(); } return true; } - /** * Actions on transition to TERMINATING * @@ -1325,17 +1369,13 @@ public class ForkJoinPool extends AbstractExecutorService { // Execution methods /** - * Common code for execute, invoke and submit + * Submits task and creates, starts, or resumes some workers if necessary */ private void doSubmit(ForkJoinTask task) { - if (task == null) - throw new NullPointerException(); - if (runState >= SHUTDOWN) - throw new RejectedExecutionException(); submissionQueue.offer(task); int c; // try to increment event count -- CAS failure OK UNSAFE.compareAndSwapInt(this, eventCountOffset, c = eventCount, c+1); - helpMaintainParallelism(); // create, start, or resume some workers + helpMaintainParallelism(); } /** @@ -1348,8 +1388,33 @@ public class ForkJoinPool extends AbstractExecutorService { * scheduled for execution */ public T invoke(ForkJoinTask task) { - doSubmit(task); - return task.join(); + if (task == null) + throw new NullPointerException(); + if (runState >= SHUTDOWN) + throw new RejectedExecutionException(); + Thread t = Thread.currentThread(); + if ((t instanceof ForkJoinWorkerThread) && + ((ForkJoinWorkerThread)t).pool == this) + return task.invoke(); // bypass submit if in same pool + else { + doSubmit(task); + return task.join(); + } + } + + /** + * Unless terminating, forks task if within an ongoing FJ + * computation in the current pool, else submits as external task. + */ + private void forkOrSubmit(ForkJoinTask task) { + if (runState >= SHUTDOWN) + throw new RejectedExecutionException(); + Thread t = Thread.currentThread(); + if ((t instanceof ForkJoinWorkerThread) && + ((ForkJoinWorkerThread)t).pool == this) + task.fork(); + else + doSubmit(task); } /** @@ -1361,7 +1426,9 @@ public class ForkJoinPool extends AbstractExecutorService { * scheduled for execution */ public void execute(ForkJoinTask task) { - doSubmit(task); + if (task == null) + throw new NullPointerException(); + forkOrSubmit(task); } // AbstractExecutorService methods @@ -1372,12 +1439,14 @@ public class ForkJoinPool extends AbstractExecutorService { * scheduled for execution */ public void execute(Runnable task) { + if (task == null) + throw new NullPointerException(); ForkJoinTask job; if (task instanceof ForkJoinTask) // avoid re-wrap job = (ForkJoinTask) task; else job = ForkJoinTask.adapt(task, null); - doSubmit(job); + forkOrSubmit(job); } /** @@ -1390,7 +1459,9 @@ public class ForkJoinPool extends AbstractExecutorService { * scheduled for execution */ public ForkJoinTask submit(ForkJoinTask task) { - doSubmit(task); + if (task == null) + throw new NullPointerException(); + forkOrSubmit(task); return task; } @@ -1400,8 +1471,10 @@ public class ForkJoinPool extends AbstractExecutorService { * scheduled for execution */ public ForkJoinTask submit(Callable task) { + if (task == null) + throw new NullPointerException(); ForkJoinTask job = ForkJoinTask.adapt(task); - doSubmit(job); + forkOrSubmit(job); return job; } @@ -1411,8 +1484,10 @@ public class ForkJoinPool extends AbstractExecutorService { * scheduled for execution */ public ForkJoinTask submit(Runnable task, T result) { + if (task == null) + throw new NullPointerException(); ForkJoinTask job = ForkJoinTask.adapt(task, result); - doSubmit(job); + forkOrSubmit(job); return job; } @@ -1422,12 +1497,14 @@ public class ForkJoinPool extends AbstractExecutorService { * scheduled for execution */ public ForkJoinTask submit(Runnable task) { + if (task == null) + throw new NullPointerException(); ForkJoinTask job; if (task instanceof ForkJoinTask) // avoid re-wrap job = (ForkJoinTask) task; else job = ForkJoinTask.adapt(task, null); - doSubmit(job); + forkOrSubmit(job); return job; } @@ -1725,8 +1802,11 @@ public class ForkJoinPool extends AbstractExecutorService { * commenced but not yet completed. This method may be useful for * debugging. A return of {@code true} reported a sufficient * period after shutdown may indicate that submitted tasks have - * ignored or suppressed interruption, causing this executor not - * to properly terminate. + * ignored or suppressed interruption, or are waiting for IO, + * causing this executor not to properly terminate. (See the + * advisory notes for class {@link ForkJoinTask} stating that + * tasks should not normally entail blocking operations. But if + * they do, they must abort them on interrupt.) * * @return {@code true} if terminating but not yet terminated */ @@ -1764,10 +1844,11 @@ public class ForkJoinPool extends AbstractExecutorService { public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { try { - return termination.awaitAdvanceInterruptibly(0, timeout, unit) > 0; + termination.awaitAdvanceInterruptibly(0, timeout, unit); } catch (TimeoutException ex) { return false; } + return true; } /** diff --git a/jdk/src/share/classes/java/util/concurrent/ForkJoinTask.java b/jdk/src/share/classes/java/util/concurrent/ForkJoinTask.java index cd18360f83d..b02323ffd6d 100644 --- a/jdk/src/share/classes/java/util/concurrent/ForkJoinTask.java +++ b/jdk/src/share/classes/java/util/concurrent/ForkJoinTask.java @@ -42,6 +42,16 @@ import java.util.List; import java.util.RandomAccess; import java.util.Map; import java.util.WeakHashMap; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.RunnableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * Abstract base class for tasks that run within a {@link ForkJoinPool}. @@ -129,6 +139,16 @@ import java.util.WeakHashMap; * result in exceptions or errors, possibly including * {@code ClassCastException}. * + *

Method {@link #join} and its variants are appropriate for use + * only when completion dependencies are acyclic; that is, the + * parallel computation can be described as a directed acyclic graph + * (DAG). Otherwise, executions may encounter a form of deadlock as + * tasks cyclically wait for each other. However, this framework + * supports other methods and techniques (for example the use of + * {@link Phaser}, {@link #helpQuiesce}, and {@link #complete}) that + * may be of use in constructing custom subclasses for problems that + * are not statically structured as DAGs. + * *

Most base support methods are {@code final}, to prevent * overriding of implementations that are intrinsically tied to the * underlying lightweight task scheduling framework. Developers @@ -143,9 +163,10 @@ import java.util.WeakHashMap; * computation. Large tasks should be split into smaller subtasks, * usually via recursive decomposition. As a very rough rule of thumb, * a task should perform more than 100 and less than 10000 basic - * computational steps. If tasks are too big, then parallelism cannot - * improve throughput. If too small, then memory and internal task - * maintenance overhead may overwhelm processing. + * computational steps, and should avoid indefinite looping. If tasks + * are too big, then parallelism cannot improve throughput. If too + * small, then memory and internal task maintenance overhead may + * overwhelm processing. * *

This class provides {@code adapt} methods for {@link Runnable} * and {@link Callable}, that may be of use when mixing execution of @@ -241,66 +262,84 @@ public abstract class ForkJoinTask implements Future, Serializable { setCompletion(EXCEPTIONAL); } - /** - * Blocks a worker thread until completion. Called only by - * pool. Currently unused -- pool-based waits use timeout - * version below. - */ - final void internalAwaitDone() { - int s; // the odd construction reduces lock bias effects - while ((s = status) >= 0) { - try { - synchronized (this) { - if (UNSAFE.compareAndSwapInt(this, statusOffset, s,SIGNAL)) - wait(); - } - } catch (InterruptedException ie) { - cancelIfTerminating(); - } - } - } - /** * Blocks a worker thread until completed or timed out. Called * only by pool. - * - * @return status on exit */ - final int internalAwaitDone(long millis) { - int s; - if ((s = status) >= 0) { - try { + final void internalAwaitDone(long millis, int nanos) { + int s = status; + if ((s == 0 && + UNSAFE.compareAndSwapInt(this, statusOffset, 0, SIGNAL)) || + s > 0) { + try { // the odd construction reduces lock bias effects synchronized (this) { - if (UNSAFE.compareAndSwapInt(this, statusOffset, s,SIGNAL)) - wait(millis, 0); + if (status > 0) + wait(millis, nanos); + else + notifyAll(); } } catch (InterruptedException ie) { cancelIfTerminating(); } - s = status; } - return s; } /** * Blocks a non-worker-thread until completion. */ private void externalAwaitDone() { - int s; - while ((s = status) >= 0) { + if (status >= 0) { + boolean interrupted = false; synchronized (this) { - if (UNSAFE.compareAndSwapInt(this, statusOffset, s, SIGNAL)){ - boolean interrupted = false; - while (status >= 0) { + for (;;) { + int s = status; + if (s == 0) + UNSAFE.compareAndSwapInt(this, statusOffset, + 0, SIGNAL); + else if (s < 0) { + notifyAll(); + break; + } + else { try { wait(); } catch (InterruptedException ie) { interrupted = true; } } - if (interrupted) - Thread.currentThread().interrupt(); - break; + } + } + if (interrupted) + Thread.currentThread().interrupt(); + } + } + + /** + * Blocks a non-worker-thread until completion or interruption or timeout. + */ + private void externalInterruptibleAwaitDone(boolean timed, long nanos) + throws InterruptedException { + if (Thread.interrupted()) + throw new InterruptedException(); + if (status >= 0) { + long startTime = timed ? System.nanoTime() : 0L; + synchronized (this) { + for (;;) { + long nt; + int s = status; + if (s == 0) + UNSAFE.compareAndSwapInt(this, statusOffset, + 0, SIGNAL); + else if (s < 0) { + notifyAll(); + break; + } + else if (!timed) + wait(); + else if ((nt = nanos - (System.nanoTime()-startTime)) > 0L) + wait(nt / 1000000, (int)(nt % 1000000)); + else + break; } } } @@ -335,7 +374,7 @@ public abstract class ForkJoinTask implements Future, Serializable { * #isDone} returning {@code true}. * *

This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * ForkJoinPool} computations (as may be determined using method * {@link #inForkJoinPool}). Attempts to invoke in other contexts * result in exceptions or errors, possibly including {@code * ClassCastException}. @@ -349,10 +388,13 @@ public abstract class ForkJoinTask implements Future, Serializable { } /** - * Returns the result of the computation when it {@link #isDone is done}. - * This method differs from {@link #get()} in that + * Returns the result of the computation when it {@link #isDone is + * done}. This method differs from {@link #get()} in that * abnormal completion results in {@code RuntimeException} or - * {@code Error}, not {@code ExecutionException}. + * {@code Error}, not {@code ExecutionException}, and that + * interrupts of the calling thread do not cause the + * method to abruptly return by throwing {@code + * InterruptedException}. * * @return the computed result */ @@ -394,7 +436,7 @@ public abstract class ForkJoinTask implements Future, Serializable { * unprocessed. * *

This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * ForkJoinPool} computations (as may be determined using method * {@link #inForkJoinPool}). Attempts to invoke in other contexts * result in exceptions or errors, possibly including {@code * ClassCastException}. @@ -422,7 +464,7 @@ public abstract class ForkJoinTask implements Future, Serializable { * normally or exceptionally, or left unprocessed. * *

This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * ForkJoinPool} computations (as may be determined using method * {@link #inForkJoinPool}). Attempts to invoke in other contexts * result in exceptions or errors, possibly including {@code * ClassCastException}. @@ -477,7 +519,7 @@ public abstract class ForkJoinTask implements Future, Serializable { * unprocessed. * *

This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * ForkJoinPool} computations (as may be determined using method * {@link #inForkJoinPool}). Attempts to invoke in other contexts * result in exceptions or errors, possibly including {@code * ClassCastException}. @@ -529,25 +571,28 @@ public abstract class ForkJoinTask implements Future, Serializable { /** * Attempts to cancel execution of this task. This attempt will - * fail if the task has already completed, has already been - * cancelled, or could not be cancelled for some other reason. If - * successful, and this task has not started when cancel is - * called, execution of this task is suppressed, {@link - * #isCancelled} will report true, and {@link #join} will result - * in a {@code CancellationException} being thrown. + * fail if the task has already completed or could not be + * cancelled for some other reason. If successful, and this task + * has not started when {@code cancel} is called, execution of + * this task is suppressed. After this method returns + * successfully, unless there is an intervening call to {@link + * #reinitialize}, subsequent calls to {@link #isCancelled}, + * {@link #isDone}, and {@code cancel} will return {@code true} + * and calls to {@link #join} and related methods will result in + * {@code CancellationException}. * *

This method may be overridden in subclasses, but if so, must - * still ensure that these minimal properties hold. In particular, - * the {@code cancel} method itself must not throw exceptions. + * still ensure that these properties hold. In particular, the + * {@code cancel} method itself must not throw exceptions. * *

This method is designed to be invoked by other * tasks. To terminate the current task, you can just return or * throw an unchecked exception from its computation method, or * invoke {@link #completeExceptionally}. * - * @param mayInterruptIfRunning this value is ignored in the - * default implementation because tasks are not - * cancelled via interruption + * @param mayInterruptIfRunning this value has no effect in the + * default implementation because interrupts are not used to + * control cancellation. * * @return {@code true} if this task is now cancelled */ @@ -681,23 +726,13 @@ public abstract class ForkJoinTask implements Future, Serializable { * member of a ForkJoinPool and was interrupted while waiting */ public final V get() throws InterruptedException, ExecutionException { - int s; - if (Thread.currentThread() instanceof ForkJoinWorkerThread) { + Thread t = Thread.currentThread(); + if (t instanceof ForkJoinWorkerThread) quietlyJoin(); - s = status; - } - else { - while ((s = status) >= 0) { - synchronized (this) { // interruptible form of awaitDone - if (UNSAFE.compareAndSwapInt(this, statusOffset, - s, SIGNAL)) { - while (status >= 0) - wait(); - } - } - } - } - if (s < NORMAL) { + else + externalInterruptibleAwaitDone(false, 0L); + int s = status; + if (s != NORMAL) { Throwable ex; if (s == CANCELLED) throw new CancellationException(); @@ -723,72 +758,18 @@ public abstract class ForkJoinTask implements Future, Serializable { */ public final V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - Thread t = Thread.currentThread(); - ForkJoinPool pool; - if (t instanceof ForkJoinWorkerThread) { - ForkJoinWorkerThread w = (ForkJoinWorkerThread) t; - if (status >= 0 && w.unpushTask(this)) - quietlyExec(); - pool = w.pool; - } - else - pool = null; - /* - * Timed wait loop intermixes cases for FJ (pool != null) and - * non FJ threads. For FJ, decrement pool count but don't try - * for replacement; increment count on completion. For non-FJ, - * deal with interrupts. This is messy, but a little less so - * than is splitting the FJ and nonFJ cases. - */ - boolean interrupted = false; - boolean dec = false; // true if pool count decremented long nanos = unit.toNanos(timeout); - for (;;) { - if (pool == null && Thread.interrupted()) { - interrupted = true; - break; - } - int s = status; - if (s < 0) - break; - if (UNSAFE.compareAndSwapInt(this, statusOffset, s, SIGNAL)) { - long startTime = System.nanoTime(); - long nt; // wait time - while (status >= 0 && - (nt = nanos - (System.nanoTime() - startTime)) > 0) { - if (pool != null && !dec) - dec = pool.tryDecrementRunningCount(); - else { - long ms = nt / 1000000; - int ns = (int) (nt % 1000000); - try { - synchronized (this) { - if (status >= 0) - wait(ms, ns); - } - } catch (InterruptedException ie) { - if (pool != null) - cancelIfTerminating(); - else { - interrupted = true; - break; - } - } - } - } - break; - } - } - if (pool != null && dec) - pool.incrementRunningCount(); - if (interrupted) - throw new InterruptedException(); - int es = status; - if (es != NORMAL) { + Thread t = Thread.currentThread(); + if (t instanceof ForkJoinWorkerThread) + ((ForkJoinWorkerThread)t).joinTask(this, true, nanos); + else + externalInterruptibleAwaitDone(true, nanos); + int s = status; + if (s != NORMAL) { Throwable ex; - if (es == CANCELLED) + if (s == CANCELLED) throw new CancellationException(); - if (es == EXCEPTIONAL && (ex = exceptionMap.get(this)) != null) + if (s == EXCEPTIONAL && (ex = exceptionMap.get(this)) != null) throw new ExecutionException(ex); throw new TimeoutException(); } @@ -819,7 +800,7 @@ public abstract class ForkJoinTask implements Future, Serializable { return; } } - w.joinTask(this); + w.joinTask(this, false, 0L); } } else @@ -855,7 +836,7 @@ public abstract class ForkJoinTask implements Future, Serializable { * processed. * *

This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * ForkJoinPool} computations (as may be determined using method * {@link #inForkJoinPool}). Attempts to invoke in other contexts * result in exceptions or errors, possibly including {@code * ClassCastException}. @@ -874,6 +855,12 @@ public abstract class ForkJoinTask implements Future, Serializable { * under any other usage conditions are not guaranteed. * This method may be useful when executing * pre-constructed trees of subtasks in loops. + * + *

Upon completion of this method, {@code isDone()} reports + * {@code false}, and {@code getException()} reports {@code + * null}. However, the value returned by {@code getRawResult} is + * unaffected. To clear this value, you can invoke {@code + * setRawResult(null)}. */ public void reinitialize() { if (status == EXCEPTIONAL) @@ -895,11 +882,12 @@ public abstract class ForkJoinTask implements Future, Serializable { } /** - * Returns {@code true} if the current thread is executing as a - * ForkJoinPool computation. + * Returns {@code true} if the current thread is a {@link + * ForkJoinWorkerThread} executing as a ForkJoinPool computation. * - * @return {@code true} if the current thread is executing as a - * ForkJoinPool computation, or false otherwise + * @return {@code true} if the current thread is a {@link + * ForkJoinWorkerThread} executing as a ForkJoinPool computation, + * or {@code false} otherwise */ public static boolean inForkJoinPool() { return Thread.currentThread() instanceof ForkJoinWorkerThread; @@ -914,7 +902,7 @@ public abstract class ForkJoinTask implements Future, Serializable { * were not, stolen. * *

This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * ForkJoinPool} computations (as may be determined using method * {@link #inForkJoinPool}). Attempts to invoke in other contexts * result in exceptions or errors, possibly including {@code * ClassCastException}. @@ -933,7 +921,7 @@ public abstract class ForkJoinTask implements Future, Serializable { * fork other tasks. * *

This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * ForkJoinPool} computations (as may be determined using method * {@link #inForkJoinPool}). Attempts to invoke in other contexts * result in exceptions or errors, possibly including {@code * ClassCastException}. @@ -956,7 +944,7 @@ public abstract class ForkJoinTask implements Future, Serializable { * exceeded. * *

This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * ForkJoinPool} computations (as may be determined using method * {@link #inForkJoinPool}). Attempts to invoke in other contexts * result in exceptions or errors, possibly including {@code * ClassCastException}. @@ -1014,7 +1002,7 @@ public abstract class ForkJoinTask implements Future, Serializable { * otherwise. * *

This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * ForkJoinPool} computations (as may be determined using method * {@link #inForkJoinPool}). Attempts to invoke in other contexts * result in exceptions or errors, possibly including {@code * ClassCastException}. @@ -1033,7 +1021,7 @@ public abstract class ForkJoinTask implements Future, Serializable { * be useful otherwise. * *

This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * ForkJoinPool} computations (as may be determined using method * {@link #inForkJoinPool}). Attempts to invoke in other contexts * result in exceptions or errors, possibly including {@code * ClassCastException}. @@ -1056,7 +1044,7 @@ public abstract class ForkJoinTask implements Future, Serializable { * otherwise. * *

This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * ForkJoinPool} computations (as may be determined using method * {@link #inForkJoinPool}). Attempts to invoke in other contexts * result in exceptions or errors, possibly including {@code * ClassCastException}. diff --git a/jdk/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java b/jdk/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java index 4dcfbbd571c..7d79c5190e2 100644 --- a/jdk/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java +++ b/jdk/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java @@ -38,16 +38,18 @@ package java.util.concurrent; import java.util.Random; import java.util.Collection; import java.util.concurrent.locks.LockSupport; +import java.util.concurrent.RejectedExecutionException; /** - * A thread managed by a {@link ForkJoinPool}. This class is - * subclassable solely for the sake of adding functionality -- there - * are no overridable methods dealing with scheduling or execution. - * However, you can override initialization and termination methods - * surrounding the main task processing loop. If you do create such a - * subclass, you will also need to supply a custom {@link - * ForkJoinPool.ForkJoinWorkerThreadFactory} to use it in a {@code - * ForkJoinPool}. + * A thread managed by a {@link ForkJoinPool}, which executes + * {@link ForkJoinTask}s. + * This class is subclassable solely for the sake of adding + * functionality -- there are no overridable methods dealing with + * scheduling or execution. However, you can override initialization + * and termination methods surrounding the main task processing loop. + * If you do create such a subclass, you will also need to supply a + * custom {@link ForkJoinPool.ForkJoinWorkerThreadFactory} to use it + * in a {@code ForkJoinPool}. * * @since 1.7 * @author Doug Lea @@ -376,7 +378,7 @@ public class ForkJoinWorkerThread extends Thread { /** * Initializes internal state after construction but before * processing any tasks. If you override this method, you must - * invoke @code{super.onStart()} at the beginning of the method. + * invoke {@code super.onStart()} at the beginning of the method. * Initialization requires care: Most fields must have legal * default values, to ensure that attempted accesses from other * threads work correctly even before this thread starts @@ -384,7 +386,7 @@ public class ForkJoinWorkerThread extends Thread { */ protected void onStart() { int rs = seedGenerator.nextInt(); - seed = rs == 0? 1 : rs; // seed must be nonzero + seed = (rs == 0) ? 1 : rs; // seed must be nonzero // Allocate name string and arrays in this thread String pid = Integer.toString(pool.getPoolNumber()); @@ -426,7 +428,7 @@ public class ForkJoinWorkerThread extends Thread { /** * This method is required to be public, but should never be * called explicitly. It performs the main run loop to execute - * ForkJoinTasks. + * {@link ForkJoinTask}s. */ public void run() { Throwable exception = null; @@ -628,6 +630,19 @@ public class ForkJoinWorkerThread extends Thread { if (t == null) // lost to stealer break; if (UNSAFE.compareAndSwapObject(q, u, t, null)) { + /* + * Note: here and in related methods, as a + * performance (not correctness) issue, we'd like + * to encourage compiler not to arbitrarily + * postpone setting sp after successful CAS. + * Currently there is no intrinsic for arranging + * this, but using Unsafe putOrderedInt may be a + * preferable strategy on some compilers even + * though its main effect is a pre-, not post- + * fence. To simplify possible changes, the option + * is left in comments next to the associated + * assignments. + */ sp = s; // putOrderedInt may encourage more timely write // UNSAFE.putOrderedInt(this, spOffset, s); return t; @@ -777,10 +792,10 @@ public class ForkJoinWorkerThread extends Thread { // Run State management // status check methods used mainly by ForkJoinPool - final boolean isRunning() { return runState == 0; } - final boolean isTerminated() { return (runState & TERMINATED) != 0; } - final boolean isSuspended() { return (runState & SUSPENDED) != 0; } - final boolean isTrimmed() { return (runState & TRIMMED) != 0; } + final boolean isRunning() { return runState == 0; } + final boolean isTerminated() { return (runState & TERMINATED) != 0; } + final boolean isSuspended() { return (runState & SUSPENDED) != 0; } + final boolean isTrimmed() { return (runState & TRIMMED) != 0; } final boolean isTerminating() { if ((runState & TERMINATING) != 0) @@ -884,8 +899,7 @@ public class ForkJoinWorkerThread extends Thread { */ final void cancelTasks() { ForkJoinTask cj = currentJoin; // try to cancel ongoing tasks - if (cj != null) { - currentJoin = null; + if (cj != null && cj.status >= 0) { cj.cancelIgnoringExceptions(); try { this.interrupt(); // awaken wait @@ -893,10 +907,8 @@ public class ForkJoinWorkerThread extends Thread { } } ForkJoinTask cs = currentSteal; - if (cs != null) { - currentSteal = null; + if (cs != null && cs.status >= 0) cs.cancelIgnoringExceptions(); - } while (base != sp) { ForkJoinTask t = deqTask(); if (t != null) @@ -959,57 +971,23 @@ public class ForkJoinWorkerThread extends Thread { * Possibly runs some tasks and/or blocks, until task is done. * * @param joinMe the task to join + * @param timed true if use timed wait + * @param nanos wait time if timed */ - final void joinTask(ForkJoinTask joinMe) { + final void joinTask(ForkJoinTask joinMe, boolean timed, long nanos) { // currentJoin only written by this thread; only need ordered store ForkJoinTask prevJoin = currentJoin; UNSAFE.putOrderedObject(this, currentJoinOffset, joinMe); - if (sp != base) - localHelpJoinTask(joinMe); - if (joinMe.status >= 0) - pool.awaitJoin(joinMe, this); + pool.awaitJoin(joinMe, this, timed, nanos); UNSAFE.putOrderedObject(this, currentJoinOffset, prevJoin); } /** - * Run tasks in local queue until given task is done. - * - * @param joinMe the task to join - */ - private void localHelpJoinTask(ForkJoinTask joinMe) { - int s; - ForkJoinTask[] q; - while (joinMe.status >= 0 && (s = sp) != base && (q = queue) != null) { - int i = (q.length - 1) & --s; - long u = (i << qShift) + qBase; // raw offset - ForkJoinTask t = q[i]; - if (t == null) // lost to a stealer - break; - if (UNSAFE.compareAndSwapObject(q, u, t, null)) { - /* - * This recheck (and similarly in helpJoinTask) - * handles cases where joinMe is independently - * cancelled or forced even though there is other work - * available. Back out of the pop by putting t back - * into slot before we commit by writing sp. - */ - if (joinMe.status < 0) { - UNSAFE.putObjectVolatile(q, u, t); - break; - } - sp = s; - // UNSAFE.putOrderedInt(this, spOffset, s); - t.quietlyExec(); - } - } - } - - /** - * Unless terminating, tries to locate and help perform tasks for - * a stealer of the given task, or in turn one of its stealers. - * Traces currentSteal->currentJoin links looking for a thread - * working on a descendant of the given task and with a non-empty - * queue to steal back and execute tasks from. + * Tries to locate and help perform tasks for a stealer of the + * given task, or in turn one of its stealers. Traces + * currentSteal->currentJoin links looking for a thread working on + * a descendant of the given task and with a non-empty queue to + * steal back and execute tasks from. * * The implementation is very branchy to cope with potential * inconsistencies or loops encountering chains that are stale, @@ -1019,77 +997,127 @@ public class ForkJoinWorkerThread extends Thread { * don't work out. * * @param joinMe the task to join + * @param running if false, then must update pool count upon + * running a task + * @return value of running on exit */ - final void helpJoinTask(ForkJoinTask joinMe) { - ForkJoinWorkerThread[] ws; - int n; - if (joinMe.status < 0) // already done - return; - if ((runState & TERMINATING) != 0) { // cancel if shutting down - joinMe.cancelIgnoringExceptions(); - return; + final boolean helpJoinTask(ForkJoinTask joinMe, boolean running) { + /* + * Initial checks to (1) abort if terminating; (2) clean out + * old cancelled tasks from local queue; (3) if joinMe is next + * task, run it; (4) omit scan if local queue nonempty (since + * it may contain non-descendents of joinMe). + */ + ForkJoinPool p = pool; + for (;;) { + ForkJoinTask[] q; + int s; + if (joinMe.status < 0) + return running; + else if ((runState & TERMINATING) != 0) { + joinMe.cancelIgnoringExceptions(); + return running; + } + else if ((s = sp) == base || (q = queue) == null) + break; // queue empty + else { + int i = (q.length - 1) & --s; + long u = (i << qShift) + qBase; // raw offset + ForkJoinTask t = q[i]; + if (t == null) + break; // lost to a stealer + else if (t != joinMe && t.status >= 0) + return running; // cannot safely help + else if ((running || + (running = p.tryIncrementRunningCount())) && + UNSAFE.compareAndSwapObject(q, u, t, null)) { + sp = s; // putOrderedInt may encourage more timely write + // UNSAFE.putOrderedInt(this, spOffset, s); + t.quietlyExec(); + } + } } - if ((ws = pool.workers) == null || (n = ws.length) <= 1) - return; // need at least 2 workers - ForkJoinTask task = joinMe; // base of chain - ForkJoinWorkerThread thread = this; // thread with stolen task - for (int d = 0; d < MAX_HELP_DEPTH; ++d) { // chain length - // Try to find v, the stealer of task, by first using hint - ForkJoinWorkerThread v = ws[thread.stealHint & (n - 1)]; - if (v == null || v.currentSteal != task) { - for (int j = 0; ; ++j) { // search array - if (j < n) { - ForkJoinTask vs; - if ((v = ws[j]) != null && - (vs = v.currentSteal) != null) { - if (joinMe.status < 0 || task.status < 0) - return; // stale or done - if (vs == task) { - thread.stealHint = j; - break; // save hint for next time + int n; // worker array size + ForkJoinWorkerThread[] ws = p.workers; + if (ws != null && (n = ws.length) > 1) { // need at least 2 workers + ForkJoinTask task = joinMe; // base of chain + ForkJoinWorkerThread thread = this; // thread with stolen task + + outer:for (int d = 0; d < MAX_HELP_DEPTH; ++d) { // chain length + // Try to find v, the stealer of task, by first using hint + ForkJoinWorkerThread v = ws[thread.stealHint & (n - 1)]; + if (v == null || v.currentSteal != task) { + for (int j = 0; ; ++j) { // search array + if (j < n) { + ForkJoinTask vs; + if ((v = ws[j]) != null && + (vs = v.currentSteal) != null) { + if (joinMe.status < 0) + break outer; + if (vs == task) { + if (task.status < 0) + break outer; // stale + thread.stealHint = j; + break; // save hint for next time + } } } + else + break outer; // no stealer } - else - return; // no stealer } - } - for (;;) { // Try to help v, using specialized form of deqTask - if (joinMe.status < 0) - return; - int b = v.base; - ForkJoinTask[] q = v.queue; - if (b == v.sp || q == null) - break; - int i = (q.length - 1) & b; - long u = (i << qShift) + qBase; - ForkJoinTask t = q[i]; - int pid = poolIndex; - ForkJoinTask ps = currentSteal; - if (task.status < 0) - return; // stale or done - if (t != null && v.base == b++ && - UNSAFE.compareAndSwapObject(q, u, t, null)) { - if (joinMe.status < 0) { - UNSAFE.putObjectVolatile(q, u, t); - return; // back out on cancel + + // Try to help v, using specialized form of deqTask + for (;;) { + if (joinMe.status < 0) + break outer; + int b = v.base; + ForkJoinTask[] q = v.queue; + if (b == v.sp || q == null) + break; // empty + int i = (q.length - 1) & b; + long u = (i << qShift) + qBase; + ForkJoinTask t = q[i]; + if (task.status < 0) + break outer; // stale + if (t != null && + (running || + (running = p.tryIncrementRunningCount())) && + v.base == b++ && + UNSAFE.compareAndSwapObject(q, u, t, null)) { + if (t != joinMe && joinMe.status < 0) { + UNSAFE.putObjectVolatile(q, u, t); + break outer; // joinMe cancelled; back out + } + v.base = b; + if (t.status >= 0) { + ForkJoinTask ps = currentSteal; + int pid = poolIndex; + v.stealHint = pid; + UNSAFE.putOrderedObject(this, + currentStealOffset, t); + t.quietlyExec(); + UNSAFE.putOrderedObject(this, + currentStealOffset, ps); + } + } + else if ((runState & TERMINATING) != 0) { + joinMe.cancelIgnoringExceptions(); + break outer; } - v.base = b; - v.stealHint = pid; - UNSAFE.putOrderedObject(this, currentStealOffset, t); - t.quietlyExec(); - UNSAFE.putOrderedObject(this, currentStealOffset, ps); } + + // Try to descend to find v's stealer + ForkJoinTask next = v.currentJoin; + if (task.status < 0 || next == null || next == task || + joinMe.status < 0) + break; // done, stale, dead-end, or cyclic + task = next; + thread = v; } - // Try to descend to find v's stealer - ForkJoinTask next = v.currentJoin; - if (task.status < 0 || next == null || next == task || - joinMe.status < 0) - return; - task = next; - thread = v; } + return running; } /** diff --git a/jdk/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java b/jdk/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java index 8051ccaa848..f5d9da1cb17 100644 --- a/jdk/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java +++ b/jdk/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java @@ -1029,6 +1029,8 @@ public class LinkedBlockingDeque * 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 reverse order */ public Iterator descendingIterator() { return new DescendingItr(); diff --git a/jdk/src/share/classes/java/util/concurrent/LinkedBlockingQueue.java b/jdk/src/share/classes/java/util/concurrent/LinkedBlockingQueue.java index 9746b7dedec..5f62e79ce56 100644 --- a/jdk/src/share/classes/java/util/concurrent/LinkedBlockingQueue.java +++ b/jdk/src/share/classes/java/util/concurrent/LinkedBlockingQueue.java @@ -189,14 +189,14 @@ public class LinkedBlockingQueue extends AbstractQueue } /** - * Creates a node and links it at end of queue. + * Links node at end of queue. * - * @param x the item + * @param node the node */ - private void enqueue(E x) { + private void enqueue(Node node) { // assert putLock.isHeldByCurrentThread(); // assert last.next == null; - last = last.next = new Node(x); + last = last.next = node; } /** @@ -282,7 +282,7 @@ public class LinkedBlockingQueue extends AbstractQueue throw new NullPointerException(); if (n == capacity) throw new IllegalStateException("Queue full"); - enqueue(e); + enqueue(new Node(e)); ++n; } count.set(n); @@ -332,6 +332,7 @@ public class LinkedBlockingQueue extends AbstractQueue // Note: convention in all put/take/etc is to preset local var // holding count negative to indicate failure unless set. int c = -1; + Node node = new Node(e); final ReentrantLock putLock = this.putLock; final AtomicInteger count = this.count; putLock.lockInterruptibly(); @@ -347,7 +348,7 @@ public class LinkedBlockingQueue extends AbstractQueue while (count.get() == capacity) { notFull.await(); } - enqueue(e); + enqueue(node); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); @@ -382,7 +383,7 @@ public class LinkedBlockingQueue extends AbstractQueue return false; nanos = notFull.awaitNanos(nanos); } - enqueue(e); + enqueue(new Node(e)); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); @@ -411,11 +412,12 @@ public class LinkedBlockingQueue extends AbstractQueue if (count.get() == capacity) return false; int c = -1; + Node node = new Node(e); final ReentrantLock putLock = this.putLock; putLock.lock(); try { if (count.get() < capacity) { - enqueue(e); + enqueue(node); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); @@ -559,6 +561,27 @@ public class LinkedBlockingQueue extends AbstractQueue } } + /** + * Returns {@code true} if this queue contains the specified element. + * More formally, returns {@code true} if and only if this queue contains + * at least one element {@code e} such that {@code o.equals(e)}. + * + * @param o object to be checked for containment in this queue + * @return {@code true} if this queue contains the specified element + */ + public boolean contains(Object o) { + if (o == null) return false; + fullyLock(); + try { + for (Node p = head.next; p != null; p = p.next) + if (o.equals(p.item)) + return true; + return false; + } finally { + fullyUnlock(); + } + } + /** * Returns an array containing all of the elements in this queue, in * proper sequence. @@ -645,7 +668,20 @@ public class LinkedBlockingQueue extends AbstractQueue public String toString() { fullyLock(); try { - return super.toString(); + Node p = head.next; + if (p == null) + return "[]"; + + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (;;) { + E e = p.item; + sb.append(e == this ? "(this Collection)" : e); + p = p.next; + if (p == null) + return sb.append(']').toString(); + sb.append(',').append(' '); + } } finally { fullyUnlock(); } @@ -727,12 +763,14 @@ public class LinkedBlockingQueue extends AbstractQueue /** * Returns an iterator over the elements in this queue in proper sequence. - * The returned {@code Iterator} is a "weakly consistent" iterator that + * The elements will be returned in order from first (head) to last (tail). + * + *

The returned 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. + * ConcurrentModificationException}, and guarantees to traverse + * elements as they existed upon construction of the iterator, and + * may (but is not guaranteed to) reflect any modifications + * subsequent to construction. * * @return an iterator over the elements in this queue in proper sequence */ diff --git a/jdk/src/share/classes/java/util/concurrent/LinkedTransferQueue.java b/jdk/src/share/classes/java/util/concurrent/LinkedTransferQueue.java index 82bed929072..fec92a8b189 100644 --- a/jdk/src/share/classes/java/util/concurrent/LinkedTransferQueue.java +++ b/jdk/src/share/classes/java/util/concurrent/LinkedTransferQueue.java @@ -37,10 +37,10 @@ package java.util.concurrent; import java.util.AbstractQueue; import java.util.Collection; -import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Queue; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; /** @@ -450,7 +450,7 @@ public class LinkedTransferQueue extends AbstractQueue } final boolean casItem(Object cmp, Object val) { - // assert cmp == null || cmp.getClass() != Node.class; + // assert cmp == null || cmp.getClass() != Node.class; return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); } @@ -516,7 +516,7 @@ public class LinkedTransferQueue extends AbstractQueue * Tries to artificially match a data node -- used by remove. */ final boolean tryMatchData() { - // assert isData; + // assert isData; Object x = item; if (x != null && x != this && casItem(x, null)) { LockSupport.unpark(waiter); @@ -569,7 +569,7 @@ public class LinkedTransferQueue extends AbstractQueue @SuppressWarnings("unchecked") static E cast(Object item) { - // assert item == null || item.getClass() != Node.class; + // assert item == null || item.getClass() != Node.class; return (E) item; } @@ -588,7 +588,8 @@ public class LinkedTransferQueue extends AbstractQueue throw new NullPointerException(); Node s = null; // the node to append, if needed - retry: for (;;) { // restart on append race + retry: + for (;;) { // restart on append race for (Node h = head, p = h; p != null;) { // find & match first node boolean isData = p.isData; @@ -599,7 +600,7 @@ public class LinkedTransferQueue extends AbstractQueue if (p.casItem(item, e)) { // match for (Node q = p; q != h;) { Node n = q.next; // update by 2 unless singleton - if (head == h && casHead(h, n == null? q : n)) { + if (head == h && casHead(h, n == null ? q : n)) { h.forgetNext(); break; } // advance and retry @@ -684,7 +685,7 @@ public class LinkedTransferQueue extends AbstractQueue for (;;) { Object item = s.item; if (item != e) { // matched - // assert item != s; + // assert item != s; s.forgetContents(); // avoid garbage return this.cast(item); } @@ -809,22 +810,61 @@ public class LinkedTransferQueue extends AbstractQueue * Moves to next node after prev, or first node if prev null. */ private void advance(Node prev) { - lastPred = lastRet; - lastRet = prev; - for (Node p = (prev == null) ? head : succ(prev); - p != null; p = succ(p)) { - Object item = p.item; - if (p.isData) { - if (item != null && item != p) { - nextItem = LinkedTransferQueue.this.cast(item); - nextNode = p; + /* + * To track and avoid buildup of deleted nodes in the face + * of calls to both Queue.remove and Itr.remove, we must + * include variants of unsplice and sweep upon each + * advance: Upon Itr.remove, we may need to catch up links + * from lastPred, and upon other removes, we might need to + * skip ahead from stale nodes and unsplice deleted ones + * found while advancing. + */ + + Node r, b; // reset lastPred upon possible deletion of lastRet + if ((r = lastRet) != null && !r.isMatched()) + lastPred = r; // next lastPred is old lastRet + else if ((b = lastPred) == null || b.isMatched()) + lastPred = null; // at start of list + else { + Node s, n; // help with removal of lastPred.next + while ((s = b.next) != null && + s != b && s.isMatched() && + (n = s.next) != null && n != s) + b.casNext(s, n); + } + + this.lastRet = prev; + + for (Node p = prev, s, n;;) { + s = (p == null) ? head : p.next; + if (s == null) + break; + else if (s == p) { + p = null; + continue; + } + Object item = s.item; + if (s.isData) { + if (item != null && item != s) { + nextItem = LinkedTransferQueue.cast(item); + nextNode = s; return; } } else if (item == null) break; + // assert s.isMatched(); + if (p == null) + p = s; + else if ((n = s.next) == null) + break; + else if (s == n) + p = null; + else + p.casNext(s, n); } nextNode = null; + nextItem = null; } Itr() { @@ -844,10 +884,12 @@ public class LinkedTransferQueue extends AbstractQueue } public final void remove() { - Node p = lastRet; - if (p == null) throw new IllegalStateException(); - if (p.tryMatchData()) - unsplice(lastPred, p); + final Node lastRet = this.lastRet; + if (lastRet == null) + throw new IllegalStateException(); + this.lastRet = null; + if (lastRet.tryMatchData()) + unsplice(lastPred, lastRet); } } @@ -997,8 +1039,7 @@ public class LinkedTransferQueue extends AbstractQueue * Inserts the specified element at the tail of this queue. * As the queue is unbounded, this method will never return {@code false}. * - * @return {@code true} (as specified by - * {@link BlockingQueue#offer(Object) BlockingQueue.offer}) + * @return {@code true} (as specified by {@link Queue#offer}) * @throws NullPointerException if the specified element is null */ public boolean offer(E e) { @@ -1130,15 +1171,15 @@ public class LinkedTransferQueue extends AbstractQueue } /** - * Returns an iterator over the elements in this queue in proper - * sequence, from head to tail. + * Returns an iterator over the elements in this queue in proper sequence. + * The elements will be returned in order from first (head) to last (tail). * *

The returned iterator is a "weakly consistent" iterator that - * will never throw - * {@link 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. + * 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 queue in proper sequence */ @@ -1202,6 +1243,28 @@ public class LinkedTransferQueue extends AbstractQueue return findAndRemove(o); } + /** + * Returns {@code true} if this queue contains the specified element. + * More formally, returns {@code true} if and only if this queue contains + * at least one element {@code e} such that {@code o.equals(e)}. + * + * @param o object to be checked for containment in this queue + * @return {@code true} if this queue contains the specified element + */ + public boolean contains(Object o) { + if (o == null) return false; + for (Node p = head; p != null; p = succ(p)) { + Object item = p.item; + if (p.isData) { + if (item != null && item != p && o.equals(item)) + return true; + } + else if (item == null) + break; + } + return false; + } + /** * Always returns {@code Integer.MAX_VALUE} because a * {@code LinkedTransferQueue} is not capacity constrained. diff --git a/jdk/src/share/classes/java/util/concurrent/Phaser.java b/jdk/src/share/classes/java/util/concurrent/Phaser.java index 623f46e41ed..d5725b3e1a2 100644 --- a/jdk/src/share/classes/java/util/concurrent/Phaser.java +++ b/jdk/src/share/classes/java/util/concurrent/Phaser.java @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; @@ -61,38 +63,38 @@ import java.util.concurrent.locks.LockSupport; * Phaser} may be repeatedly awaited. Method {@link * #arriveAndAwaitAdvance} has effect analogous to {@link * java.util.concurrent.CyclicBarrier#await CyclicBarrier.await}. Each - * generation of a {@code Phaser} has an associated phase number. The - * phase number starts at zero, and advances when all parties arrive - * at the barrier, wrapping around to zero after reaching {@code + * generation of a phaser has an associated phase number. The phase + * number starts at zero, and advances when all parties arrive at the + * phaser, wrapping around to zero after reaching {@code * Integer.MAX_VALUE}. The use of phase numbers enables independent - * control of actions upon arrival at a barrier and upon awaiting + * control of actions upon arrival at a phaser and upon awaiting * others, via two kinds of methods that may be invoked by any * registered party: * *

    * *
  • Arrival. Methods {@link #arrive} and - * {@link #arriveAndDeregister} record arrival at a - * barrier. These methods do not block, but return an associated - * arrival phase number; that is, the phase number of - * the barrier to which the arrival applied. When the final - * party for a given phase arrives, an optional barrier action - * is performed and the phase advances. Barrier actions, - * performed by the party triggering a phase advance, are - * arranged by overriding method {@link #onAdvance(int, int)}, - * which also controls termination. Overriding this method is - * similar to, but more flexible than, providing a barrier - * action to a {@code CyclicBarrier}. + * {@link #arriveAndDeregister} record arrival. These methods + * do not block, but return an associated arrival phase + * number; that is, the phase number of the phaser to which + * the arrival applied. When the final party for a given phase + * arrives, an optional action is performed and the phase + * advances. These actions are performed by the party + * triggering a phase advance, and are arranged by overriding + * method {@link #onAdvance(int, int)}, which also controls + * termination. Overriding this method is similar to, but more + * flexible than, providing a barrier action to a {@code + * CyclicBarrier}. * *
  • Waiting. Method {@link #awaitAdvance} requires an * argument indicating an arrival phase number, and returns when - * the barrier advances to (or is already at) a different phase. + * the phaser advances to (or is already at) a different phase. * Unlike similar constructions using {@code CyclicBarrier}, * method {@code awaitAdvance} continues to wait even if the * waiting thread is interrupted. Interruptible and timeout * versions are also available, but exceptions encountered while * tasks wait interruptibly or with timeout do not change the - * state of the barrier. If necessary, you can perform any + * state of the phaser. If necessary, you can perform any * associated recovery within handlers of those exceptions, * often after invoking {@code forceTermination}. Phasers may * also be used by tasks executing in a {@link ForkJoinPool}, @@ -101,26 +103,39 @@ import java.util.concurrent.locks.LockSupport; * *
* - *

Termination. A {@code Phaser} may enter a - * termination state in which all synchronization methods - * immediately return without updating phaser state or waiting for - * advance, and indicating (via a negative phase value) that execution - * is complete. Termination is triggered when an invocation of {@code - * onAdvance} returns {@code true}. As illustrated below, when - * phasers control actions with a fixed number of iterations, it is - * often convenient to override this method to cause termination when - * the current phase number reaches a threshold. Method {@link - * #forceTermination} is also available to abruptly release waiting - * threads and allow them to terminate. + *

Termination. A phaser may enter a termination + * state, that may be checked using method {@link #isTerminated}. Upon + * termination, all synchronization methods immediately return without + * waiting for advance, as indicated by a negative return value. + * Similarly, attempts to register upon termination have no effect. + * Termination is triggered when an invocation of {@code onAdvance} + * returns {@code true}. The default implementation returns {@code + * true} if a deregistration has caused the number of registered + * parties to become zero. As illustrated below, when phasers control + * actions with a fixed number of iterations, it is often convenient + * to override this method to cause termination when the current phase + * number reaches a threshold. Method {@link #forceTermination} is + * also available to abruptly release waiting threads and allow them + * to terminate. * - *

Tiering. Phasers may be tiered (i.e., arranged - * in tree structures) to reduce contention. Phasers with large - * numbers of parties that would otherwise experience heavy + *

Tiering. Phasers may be tiered (i.e., + * constructed in tree structures) to reduce contention. Phasers with + * large numbers of parties that would otherwise experience heavy * synchronization contention costs may instead be set up so that * groups of sub-phasers share a common parent. This may greatly * increase throughput even though it incurs greater per-operation * overhead. * + *

In a tree of tiered phasers, registration and deregistration of + * child phasers with their parent are managed automatically. + * Whenever the number of registered parties of a child phaser becomes + * non-zero (as established in the {@link #Phaser(Phaser,int)} + * constructor, {@link #register}, or {@link #bulkRegister}), the + * child phaser is registered with its parent. Whenever the number of + * registered parties becomes zero as the result of an invocation of + * {@link #arriveAndDeregister}, the child phaser is deregistered + * from its parent. + * *

Monitoring. While synchronization methods may be invoked * only by registered parties, the current state of a phaser may be * monitored by any caller. At any given moment there are {@link @@ -136,9 +151,9 @@ import java.util.concurrent.locks.LockSupport; *

Sample usages: * *

A {@code Phaser} may be used instead of a {@code CountDownLatch} - * to control a one-shot action serving a variable number of - * parties. The typical idiom is for the method setting this up to - * first register, then start the actions, then deregister, as in: + * to control a one-shot action serving a variable number of parties. + * The typical idiom is for the method setting this up to first + * register, then start the actions, then deregister, as in: * *

 {@code
  * void runTasks(List tasks) {
@@ -208,34 +223,32 @@ import java.util.concurrent.locks.LockSupport;
  * }}
* * - *

To create a set of tasks using a tree of phasers, - * you could use code of the following form, assuming a - * Task class with a constructor accepting a phaser that - * it registers for upon construction: + *

To create a set of {@code n} tasks using a tree of phasers, you + * could use code of the following form, assuming a Task class with a + * constructor accepting a {@code Phaser} that it registers with upon + * construction. After invocation of {@code build(new Task[n], 0, n, + * new Phaser())}, these tasks could then be started, for example by + * submitting to a pool: * *

 {@code
- * void build(Task[] actions, int lo, int hi, Phaser ph) {
+ * void build(Task[] tasks, int lo, int hi, Phaser ph) {
  *   if (hi - lo > TASKS_PER_PHASER) {
  *     for (int i = lo; i < hi; i += TASKS_PER_PHASER) {
  *       int j = Math.min(i + TASKS_PER_PHASER, hi);
- *       build(actions, i, j, new Phaser(ph));
+ *       build(tasks, i, j, new Phaser(ph));
  *     }
  *   } else {
  *     for (int i = lo; i < hi; ++i)
- *       actions[i] = new Task(ph);
+ *       tasks[i] = new Task(ph);
  *       // assumes new Task(ph) performs ph.register()
  *   }
- * }
- * // .. initially called, for n tasks via
- * build(new Task[n], 0, n, new Phaser());}
+ * }} * * The best value of {@code TASKS_PER_PHASER} depends mainly on - * expected barrier synchronization rates. A value as low as four may - * be appropriate for extremely small per-barrier task bodies (thus + * expected synchronization rates. A value as low as four may + * be appropriate for extremely small per-phase task bodies (thus * high rates), or up to hundreds for extremely large ones. * - * - * *

Implementation notes: This implementation restricts the * maximum number of parties to 65535. Attempts to register additional * parties result in {@code IllegalStateException}. However, you can and @@ -253,60 +266,66 @@ public class Phaser { */ /** - * Barrier state representation. Conceptually, a barrier contains - * four values: + * Primary state representation, holding four bit-fields: * - * * parties -- the number of parties to wait (16 bits) - * * unarrived -- the number of parties yet to hit barrier (16 bits) - * * phase -- the generation of the barrier (31 bits) - * * terminated -- set if barrier is terminated (1 bit) + * unarrived -- the number of parties yet to hit barrier (bits 0-15) + * parties -- the number of parties to wait (bits 16-31) + * phase -- the generation of the barrier (bits 32-62) + * terminated -- set if barrier is terminated (bit 63 / sign) * - * However, to efficiently maintain atomicity, these values are - * packed into a single (atomic) long. Termination uses the sign - * bit of 32 bit representation of phase, so phase is set to -1 on - * termination. Good performance relies on keeping state decoding - * and encoding simple, and keeping race windows short. + * Except that a phaser with no registered parties is + * distinguished by the otherwise illegal state of having zero + * parties and one unarrived parties (encoded as EMPTY below). * - * Note: there are some cheats in arrive() that rely on unarrived - * count being lowest 16 bits. + * To efficiently maintain atomicity, these values are packed into + * a single (atomic) long. Good performance relies on keeping + * state decoding and encoding simple, and keeping race windows + * short. + * + * All state updates are performed via CAS except initial + * registration of a sub-phaser (i.e., one with a non-null + * parent). In this (relatively rare) case, we use built-in + * synchronization to lock while first registering with its + * parent. + * + * The phase of a subphaser is allowed to lag that of its + * ancestors until it is actually accessed -- see method + * reconcileState. */ private volatile long state; - private static final int ushortMask = 0xffff; - private static final int phaseMask = 0x7fffffff; + private static final int MAX_PARTIES = 0xffff; + private static final int MAX_PHASE = Integer.MAX_VALUE; + private static final int PARTIES_SHIFT = 16; + private static final int PHASE_SHIFT = 32; + private static final int UNARRIVED_MASK = 0xffff; // to mask ints + private static final long PARTIES_MASK = 0xffff0000L; // to mask longs + private static final long TERMINATION_BIT = 1L << 63; + + // some special values + private static final int ONE_ARRIVAL = 1; + private static final int ONE_PARTY = 1 << PARTIES_SHIFT; + private static final int EMPTY = 1; + + // The following unpacking methods are usually manually inlined private static int unarrivedOf(long s) { - return (int) (s & ushortMask); + int counts = (int)s; + return (counts == EMPTY) ? 0 : counts & UNARRIVED_MASK; } private static int partiesOf(long s) { - return ((int) s) >>> 16; + return (int)s >>> PARTIES_SHIFT; } private static int phaseOf(long s) { - return (int) (s >>> 32); + return (int)(s >>> PHASE_SHIFT); } private static int arrivedOf(long s) { - return partiesOf(s) - unarrivedOf(s); - } - - private static long stateFor(int phase, int parties, int unarrived) { - return ((((long) phase) << 32) | (((long) parties) << 16) | - (long) unarrived); - } - - private static long trippedStateFor(int phase, int parties) { - long lp = (long) parties; - return (((long) phase) << 32) | (lp << 16) | lp; - } - - /** - * Returns message string for bad bounds exceptions. - */ - private static String badBounds(int parties, int unarrived) { - return ("Attempt to set " + unarrived + - " unarrived of " + parties + " parties"); + int counts = (int)s; + return (counts == EMPTY) ? 0 : + (counts >>> PARTIES_SHIFT) - (counts & UNARRIVED_MASK); } /** @@ -315,70 +334,180 @@ public class Phaser { private final Phaser parent; /** - * The root of phaser tree. Equals this if not in a tree. Used to - * support faster state push-down. + * The root of phaser tree. Equals this if not in a tree. */ private final Phaser root; - // Wait queues - /** * Heads of Treiber stacks for waiting threads. To eliminate - * contention while releasing some threads while adding others, we + * contention when releasing some threads while adding others, we * use two of them, alternating across even and odd phases. + * Subphasers share queues with root to speed up releases. */ - private final AtomicReference evenQ = new AtomicReference(); - private final AtomicReference oddQ = new AtomicReference(); + private final AtomicReference evenQ; + private final AtomicReference oddQ; private AtomicReference queueFor(int phase) { return ((phase & 1) == 0) ? evenQ : oddQ; } /** - * Returns current state, first resolving lagged propagation from - * root if necessary. + * Returns message string for bounds exceptions on arrival. */ - private long getReconciledState() { - return (parent == null) ? state : reconcileState(); + private String badArrive(long s) { + return "Attempted arrival of unregistered party for " + + stateToString(s); } /** - * Recursively resolves state. + * Returns message string for bounds exceptions on registration. */ - private long reconcileState() { - Phaser p = parent; - long s = state; - if (p != null) { - while (unarrivedOf(s) == 0 && phaseOf(s) != phaseOf(root.state)) { - long parentState = p.getReconciledState(); - int parentPhase = phaseOf(parentState); - int phase = phaseOf(s = state); - if (phase != parentPhase) { - long next = trippedStateFor(parentPhase, partiesOf(s)); - if (casState(s, next)) { - releaseWaiters(phase); - s = next; + private String badRegister(long s) { + return "Attempt to register more than " + + MAX_PARTIES + " parties for " + stateToString(s); + } + + /** + * Main implementation for methods arrive and arriveAndDeregister. + * Manually tuned to speed up and minimize race windows for the + * common case of just decrementing unarrived field. + * + * @param deregister false for arrive, true for arriveAndDeregister + */ + private int doArrive(boolean deregister) { + int adj = deregister ? ONE_ARRIVAL|ONE_PARTY : ONE_ARRIVAL; + final Phaser root = this.root; + for (;;) { + long s = (root == this) ? state : reconcileState(); + int phase = (int)(s >>> PHASE_SHIFT); + int counts = (int)s; + int unarrived = (counts & UNARRIVED_MASK) - 1; + if (phase < 0) + return phase; + else if (counts == EMPTY || unarrived < 0) { + if (root == this || reconcileState() == s) + throw new IllegalStateException(badArrive(s)); + } + else if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s-=adj)) { + if (unarrived == 0) { + long n = s & PARTIES_MASK; // base of next state + int nextUnarrived = (int)n >>> PARTIES_SHIFT; + if (root != this) + return parent.doArrive(nextUnarrived == 0); + if (onAdvance(phase, nextUnarrived)) + n |= TERMINATION_BIT; + else if (nextUnarrived == 0) + n |= EMPTY; + else + n |= nextUnarrived; + n |= (long)((phase + 1) & MAX_PHASE) << PHASE_SHIFT; + UNSAFE.compareAndSwapLong(this, stateOffset, s, n); + releaseWaiters(phase); + } + return phase; + } + } + } + + /** + * Implementation of register, bulkRegister + * + * @param registrations number to add to both parties and + * unarrived fields. Must be greater than zero. + */ + private int doRegister(int registrations) { + // adjustment to state + long adj = ((long)registrations << PARTIES_SHIFT) | registrations; + final Phaser parent = this.parent; + int phase; + for (;;) { + long s = state; + int counts = (int)s; + int parties = counts >>> PARTIES_SHIFT; + int unarrived = counts & UNARRIVED_MASK; + if (registrations > MAX_PARTIES - parties) + throw new IllegalStateException(badRegister(s)); + else if ((phase = (int)(s >>> PHASE_SHIFT)) < 0) + break; + else if (counts != EMPTY) { // not 1st registration + if (parent == null || reconcileState() == s) { + if (unarrived == 0) // wait out advance + root.internalAwaitAdvance(phase, null); + else if (UNSAFE.compareAndSwapLong(this, stateOffset, + s, s + adj)) + break; + } + } + else if (parent == null) { // 1st root registration + long next = ((long)phase << PHASE_SHIFT) | adj; + if (UNSAFE.compareAndSwapLong(this, stateOffset, s, next)) + break; + } + else { + synchronized (this) { // 1st sub registration + if (state == s) { // recheck under lock + parent.doRegister(1); + do { // force current phase + phase = (int)(root.state >>> PHASE_SHIFT); + // assert phase < 0 || (int)state == EMPTY; + } while (!UNSAFE.compareAndSwapLong + (this, stateOffset, state, + ((long)phase << PHASE_SHIFT) | adj)); + break; } } } } + return phase; + } + + /** + * Resolves lagged phase propagation from root if necessary. + * Reconciliation normally occurs when root has advanced but + * subphasers have not yet done so, in which case they must finish + * their own advance by setting unarrived to parties (or if + * parties is zero, resetting to unregistered EMPTY state). + * However, this method may also be called when "floating" + * subphasers with possibly some unarrived parties are merely + * catching up to current phase, in which case counts are + * unaffected. + * + * @return reconciled state + */ + private long reconcileState() { + final Phaser root = this.root; + long s = state; + if (root != this) { + int phase, u, p; + // CAS root phase with current parties; possibly trip unarrived + while ((phase = (int)(root.state >>> PHASE_SHIFT)) != + (int)(s >>> PHASE_SHIFT) && + !UNSAFE.compareAndSwapLong + (this, stateOffset, s, + s = (((long)phase << PHASE_SHIFT) | + (s & PARTIES_MASK) | + ((p = (int)s >>> PARTIES_SHIFT) == 0 ? EMPTY : + (u = (int)s & UNARRIVED_MASK) == 0 ? p : u)))) + s = state; + } return s; } /** - * Creates a new phaser without any initially registered parties, - * initial phase number 0, and no parent. Any thread using this + * Creates a new phaser with no initially registered parties, no + * parent, and initial phase number 0. Any thread using this * phaser will need to first register for it. */ public Phaser() { - this(null); + this(null, 0); } /** - * Creates a new phaser with the given numbers of registered - * unarrived parties, initial phase number 0, and no parent. + * Creates a new phaser with the given number of registered + * unarrived parties, no parent, and initial phase number 0. * - * @param parties the number of parties required to trip barrier + * @param parties the number of parties required to advance to the + * next phase * @throws IllegalArgumentException if parties less than zero * or greater than the maximum number of parties supported */ @@ -387,54 +516,62 @@ public class Phaser { } /** - * Creates a new phaser with the given parent, without any - * initially registered parties. If parent is non-null this phaser - * is registered with the parent and its initial phase number is - * the same as that of parent phaser. + * Equivalent to {@link #Phaser(Phaser, int) Phaser(parent, 0)}. * * @param parent the parent phaser */ public Phaser(Phaser parent) { - int phase = 0; - this.parent = parent; - if (parent != null) { - this.root = parent.root; - phase = parent.register(); - } - else - this.root = this; - this.state = trippedStateFor(phase, 0); + this(parent, 0); } /** - * Creates a new phaser with the given parent and numbers of - * registered unarrived parties. If parent is non-null, this phaser - * is registered with the parent and its initial phase number is - * the same as that of parent phaser. + * Creates a new phaser with the given parent and number of + * registered unarrived parties. When the given parent is non-null + * and the given number of parties is greater than zero, this + * child phaser is registered with its parent. * * @param parent the parent phaser - * @param parties the number of parties required to trip barrier + * @param parties the number of parties required to advance to the + * next phase * @throws IllegalArgumentException if parties less than zero * or greater than the maximum number of parties supported */ public Phaser(Phaser parent, int parties) { - if (parties < 0 || parties > ushortMask) + if (parties >>> PARTIES_SHIFT != 0) throw new IllegalArgumentException("Illegal number of parties"); int phase = 0; this.parent = parent; if (parent != null) { - this.root = parent.root; - phase = parent.register(); + final Phaser root = parent.root; + this.root = root; + this.evenQ = root.evenQ; + this.oddQ = root.oddQ; + if (parties != 0) + phase = parent.doRegister(1); } - else + else { this.root = this; - this.state = trippedStateFor(phase, parties); + this.evenQ = new AtomicReference(); + this.oddQ = new AtomicReference(); + } + this.state = (parties == 0) ? (long)EMPTY : + ((long)phase << PHASE_SHIFT) | + ((long)parties << PARTIES_SHIFT) | + ((long)parties); } /** - * Adds a new unarrived party to this phaser. + * Adds a new unarrived party to this phaser. If an ongoing + * invocation of {@link #onAdvance} is in progress, this method + * may await its completion before returning. If this phaser has + * a parent, and this phaser previously had no registered parties, + * this child phaser is also registered with its parent. If + * this phaser is terminated, the attempt to register has + * no effect, and a negative value is returned. * - * @return the arrival phase number to which this registration applied + * @return the arrival phase number to which this registration + * applied. If this value is negative, then this phaser has + * terminated, in which case registration has no effect. * @throws IllegalStateException if attempting to register more * than the maximum supported number of parties */ @@ -444,11 +581,22 @@ public class Phaser { /** * Adds the given number of new unarrived parties to this phaser. + * If an ongoing invocation of {@link #onAdvance} is in progress, + * this method may await its completion before returning. If this + * phaser has a parent, and the given number of parties is greater + * than zero, and this phaser previously had no registered + * parties, this child phaser is also registered with its parent. + * If this phaser is terminated, the attempt to register has no + * effect, and a negative value is returned. * - * @param parties the number of parties required to trip barrier - * @return the arrival phase number to which this registration applied + * @param parties the number of additional parties required to + * advance to the next phase + * @return the arrival phase number to which this registration + * applied. If this value is negative, then this phaser has + * terminated, in which case registration has no effect. * @throws IllegalStateException if attempting to register more * than the maximum supported number of parties + * @throws IllegalArgumentException if {@code parties < 0} */ public int bulkRegister(int parties) { if (parties < 0) @@ -459,258 +607,210 @@ public class Phaser { } /** - * Shared code for register, bulkRegister - */ - private int doRegister(int registrations) { - int phase; - for (;;) { - long s = getReconciledState(); - phase = phaseOf(s); - int unarrived = unarrivedOf(s) + registrations; - int parties = partiesOf(s) + registrations; - if (phase < 0) - break; - if (parties > ushortMask || unarrived > ushortMask) - throw new IllegalStateException(badBounds(parties, unarrived)); - if (phase == phaseOf(root.state) && - casState(s, stateFor(phase, parties, unarrived))) - break; - } - return phase; - } - - /** - * Arrives at the barrier, but does not wait for others. (You can - * in turn wait for others via {@link #awaitAdvance}). It is an - * unenforced usage error for an unregistered party to invoke this - * method. + * Arrives at this phaser, without waiting for others to arrive. + * + *

It is a usage error for an unregistered party to invoke this + * method. However, this error may result in an {@code + * IllegalStateException} only upon some subsequent operation on + * this phaser, if ever. * * @return the arrival phase number, or a negative value if terminated * @throws IllegalStateException if not terminated and the number * of unarrived parties would become negative */ public int arrive() { - int phase; - for (;;) { - long s = state; - phase = phaseOf(s); - if (phase < 0) - break; - int parties = partiesOf(s); - int unarrived = unarrivedOf(s) - 1; - if (unarrived > 0) { // Not the last arrival - if (casState(s, s - 1)) // s-1 adds one arrival - break; - } - else if (unarrived == 0) { // the last arrival - Phaser par = parent; - if (par == null) { // directly trip - if (casState - (s, - trippedStateFor(onAdvance(phase, parties) ? -1 : - ((phase + 1) & phaseMask), parties))) { - releaseWaiters(phase); - break; - } - } - else { // cascade to parent - if (casState(s, s - 1)) { // zeroes unarrived - par.arrive(); - reconcileState(); - break; - } - } - } - else if (phase != phaseOf(root.state)) // or if unreconciled - reconcileState(); - else - throw new IllegalStateException(badBounds(parties, unarrived)); - } - return phase; + return doArrive(false); } /** - * Arrives at the barrier and deregisters from it without waiting - * for others. Deregistration reduces the number of parties - * required to trip the barrier in future phases. If this phaser + * Arrives at this phaser and deregisters from it without waiting + * for others to arrive. Deregistration reduces the number of + * parties required to advance in future phases. If this phaser * has a parent, and deregistration causes this phaser to have - * zero parties, this phaser also arrives at and is deregistered - * from its parent. It is an unenforced usage error for an - * unregistered party to invoke this method. + * zero parties, this phaser is also deregistered from its parent. + * + *

It is a usage error for an unregistered party to invoke this + * method. However, this error may result in an {@code + * IllegalStateException} only upon some subsequent operation on + * this phaser, if ever. * * @return the arrival phase number, or a negative value if terminated * @throws IllegalStateException if not terminated and the number * of registered or unarrived parties would become negative */ public int arriveAndDeregister() { - // similar code to arrive, but too different to merge - Phaser par = parent; - int phase; - for (;;) { - long s = state; - phase = phaseOf(s); - if (phase < 0) - break; - int parties = partiesOf(s) - 1; - int unarrived = unarrivedOf(s) - 1; - if (parties >= 0) { - if (unarrived > 0 || (unarrived == 0 && par != null)) { - if (casState - (s, - stateFor(phase, parties, unarrived))) { - if (unarrived == 0) { - par.arriveAndDeregister(); - reconcileState(); - } - break; - } - continue; - } - if (unarrived == 0) { - if (casState - (s, - trippedStateFor(onAdvance(phase, parties) ? -1 : - ((phase + 1) & phaseMask), parties))) { - releaseWaiters(phase); - break; - } - continue; - } - if (par != null && phase != phaseOf(root.state)) { - reconcileState(); - continue; - } - } - throw new IllegalStateException(badBounds(parties, unarrived)); - } - return phase; + return doArrive(true); } /** - * Arrives at the barrier and awaits others. Equivalent in effect + * Arrives at this phaser and awaits others. Equivalent in effect * to {@code awaitAdvance(arrive())}. If you need to await with * interruption or timeout, you can arrange this with an analogous - * construction using one of the other forms of the awaitAdvance - * method. If instead you need to deregister upon arrival use - * {@code arriveAndDeregister}. It is an unenforced usage error - * for an unregistered party to invoke this method. + * construction using one of the other forms of the {@code + * awaitAdvance} method. If instead you need to deregister upon + * arrival, use {@code awaitAdvance(arriveAndDeregister())}. * - * @return the arrival phase number, or a negative number if terminated + *

It is a usage error for an unregistered party to invoke this + * method. However, this error may result in an {@code + * IllegalStateException} only upon some subsequent operation on + * this phaser, if ever. + * + * @return the arrival phase number, or the (negative) + * {@linkplain #getPhase() current phase} if terminated * @throws IllegalStateException if not terminated and the number * of unarrived parties would become negative */ public int arriveAndAwaitAdvance() { - return awaitAdvance(arrive()); + // Specialization of doArrive+awaitAdvance eliminating some reads/paths + final Phaser root = this.root; + for (;;) { + long s = (root == this) ? state : reconcileState(); + int phase = (int)(s >>> PHASE_SHIFT); + int counts = (int)s; + int unarrived = (counts & UNARRIVED_MASK) - 1; + if (phase < 0) + return phase; + else if (counts == EMPTY || unarrived < 0) { + if (reconcileState() == s) + throw new IllegalStateException(badArrive(s)); + } + else if (UNSAFE.compareAndSwapLong(this, stateOffset, s, + s -= ONE_ARRIVAL)) { + if (unarrived != 0) + return root.internalAwaitAdvance(phase, null); + if (root != this) + return parent.arriveAndAwaitAdvance(); + long n = s & PARTIES_MASK; // base of next state + int nextUnarrived = (int)n >>> PARTIES_SHIFT; + if (onAdvance(phase, nextUnarrived)) + n |= TERMINATION_BIT; + else if (nextUnarrived == 0) + n |= EMPTY; + else + n |= nextUnarrived; + int nextPhase = (phase + 1) & MAX_PHASE; + n |= (long)nextPhase << PHASE_SHIFT; + if (!UNSAFE.compareAndSwapLong(this, stateOffset, s, n)) + return (int)(state >>> PHASE_SHIFT); // terminated + releaseWaiters(phase); + return nextPhase; + } + } } /** - * Awaits the phase of the barrier to advance from the given phase - * value, returning immediately if the current phase of the - * barrier is not equal to the given phase value or this barrier - * is terminated. It is an unenforced usage error for an - * unregistered party to invoke this method. + * Awaits the phase of this phaser to advance from the given phase + * value, returning immediately if the current phase is not equal + * to the given phase value or this phaser is terminated. * * @param phase an arrival phase number, or negative value if * terminated; this argument is normally the value returned by a - * previous call to {@code arrive} or its variants - * @return the next arrival phase number, or a negative value - * if terminated or argument is negative + * previous call to {@code arrive} or {@code arriveAndDeregister}. + * @return the next arrival phase number, or the argument if it is + * negative, or the (negative) {@linkplain #getPhase() current phase} + * if terminated */ public int awaitAdvance(int phase) { + final Phaser root = this.root; + long s = (root == this) ? state : reconcileState(); + int p = (int)(s >>> PHASE_SHIFT); if (phase < 0) return phase; - long s = getReconciledState(); - int p = phaseOf(s); - if (p != phase) - return p; - if (unarrivedOf(s) == 0 && parent != null) - parent.awaitAdvance(phase); - // Fall here even if parent waited, to reconcile and help release - return untimedWait(phase); + if (p == phase) + return root.internalAwaitAdvance(phase, null); + return p; } /** - * Awaits the phase of the barrier to advance from the given phase + * Awaits the phase of this phaser to advance from the given phase * value, throwing {@code InterruptedException} if interrupted - * while waiting, or returning immediately if the current phase of - * the barrier is not equal to the given phase value or this - * barrier is terminated. It is an unenforced usage error for an - * unregistered party to invoke this method. + * while waiting, or returning immediately if the current phase is + * not equal to the given phase value or this phaser is + * terminated. * * @param phase an arrival phase number, or negative value if * terminated; this argument is normally the value returned by a - * previous call to {@code arrive} or its variants - * @return the next arrival phase number, or a negative value - * if terminated or argument is negative + * previous call to {@code arrive} or {@code arriveAndDeregister}. + * @return the next arrival phase number, or the argument if it is + * negative, or the (negative) {@linkplain #getPhase() current phase} + * if terminated * @throws InterruptedException if thread interrupted while waiting */ public int awaitAdvanceInterruptibly(int phase) throws InterruptedException { + final Phaser root = this.root; + long s = (root == this) ? state : reconcileState(); + int p = (int)(s >>> PHASE_SHIFT); if (phase < 0) return phase; - long s = getReconciledState(); - int p = phaseOf(s); - if (p != phase) - return p; - if (unarrivedOf(s) == 0 && parent != null) - parent.awaitAdvanceInterruptibly(phase); - return interruptibleWait(phase); + if (p == phase) { + QNode node = new QNode(this, phase, true, false, 0L); + p = root.internalAwaitAdvance(phase, node); + if (node.wasInterrupted) + throw new InterruptedException(); + } + return p; } /** - * Awaits the phase of the barrier to advance from the given phase + * Awaits the phase of this phaser to advance from the given phase * value or the given timeout to elapse, throwing {@code * InterruptedException} if interrupted while waiting, or - * returning immediately if the current phase of the barrier is - * not equal to the given phase value or this barrier is - * terminated. It is an unenforced usage error for an - * unregistered party to invoke this method. + * returning immediately if the current phase is not equal to the + * given phase value or this phaser is terminated. * * @param phase an arrival phase number, or negative value if * terminated; this argument is normally the value returned by a - * previous call to {@code arrive} or its variants + * previous call to {@code arrive} or {@code arriveAndDeregister}. * @param timeout how long to wait before giving up, in units of * {@code unit} * @param unit a {@code TimeUnit} determining how to interpret the * {@code timeout} parameter - * @return the next arrival phase number, or a negative value - * if terminated or argument is negative + * @return the next arrival phase number, or the argument if it is + * negative, or the (negative) {@linkplain #getPhase() current phase} + * if terminated * @throws InterruptedException if thread interrupted while waiting * @throws TimeoutException if timed out while waiting */ public int awaitAdvanceInterruptibly(int phase, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { + long nanos = unit.toNanos(timeout); + final Phaser root = this.root; + long s = (root == this) ? state : reconcileState(); + int p = (int)(s >>> PHASE_SHIFT); if (phase < 0) return phase; - long s = getReconciledState(); - int p = phaseOf(s); - if (p != phase) - return p; - if (unarrivedOf(s) == 0 && parent != null) - parent.awaitAdvanceInterruptibly(phase, timeout, unit); - return timedWait(phase, unit.toNanos(timeout)); + if (p == phase) { + QNode node = new QNode(this, phase, true, true, nanos); + p = root.internalAwaitAdvance(phase, node); + if (node.wasInterrupted) + throw new InterruptedException(); + else if (p == phase) + throw new TimeoutException(); + } + return p; } /** - * Forces this barrier to enter termination state. Counts of - * arrived and registered parties are unaffected. If this phaser - * has a parent, it too is terminated. This method may be useful - * for coordinating recovery after one or more tasks encounter + * Forces this phaser to enter termination state. Counts of + * registered parties are unaffected. If this phaser is a member + * of a tiered set of phasers, then all of the phasers in the set + * are terminated. If this phaser is already terminated, this + * method has no effect. This method may be useful for + * coordinating recovery after one or more tasks encounter * unexpected exceptions. */ public void forceTermination() { - for (;;) { - long s = getReconciledState(); - int phase = phaseOf(s); - int parties = partiesOf(s); - int unarrived = unarrivedOf(s); - if (phase < 0 || - casState(s, stateFor(-1, parties, unarrived))) { + // Only need to change root state + final Phaser root = this.root; + long s; + while ((s = root.state) >= 0) { + if (UNSAFE.compareAndSwapLong(root, stateOffset, + s, s | TERMINATION_BIT)) { + // signal all threads releaseWaiters(0); releaseWaiters(1); - if (parent != null) - parent.forceTermination(); return; } } @@ -719,16 +819,18 @@ public class Phaser { /** * Returns the current phase number. The maximum phase number is * {@code Integer.MAX_VALUE}, after which it restarts at - * zero. Upon termination, the phase number is negative. + * zero. Upon termination, the phase number is negative, + * in which case the prevailing phase prior to termination + * may be obtained via {@code getPhase() + Integer.MIN_VALUE}. * * @return the phase number, or a negative value if terminated */ public final int getPhase() { - return phaseOf(getReconciledState()); + return (int)(root.state >>> PHASE_SHIFT); } /** - * Returns the number of parties registered at this barrier. + * Returns the number of parties registered at this phaser. * * @return the number of parties */ @@ -738,22 +840,24 @@ public class Phaser { /** * Returns the number of registered parties that have arrived at - * the current phase of this barrier. + * the current phase of this phaser. If this phaser has terminated, + * the returned value is meaningless and arbitrary. * * @return the number of arrived parties */ public int getArrivedParties() { - return arrivedOf(state); + return arrivedOf(reconcileState()); } /** * Returns the number of registered parties that have not yet - * arrived at the current phase of this barrier. + * arrived at the current phase of this phaser. If this phaser has + * terminated, the returned value is meaningless and arbitrary. * * @return the number of unarrived parties */ public int getUnarrivedParties() { - return unarrivedOf(state); + return unarrivedOf(reconcileState()); } /** @@ -776,52 +880,56 @@ public class Phaser { } /** - * Returns {@code true} if this barrier has been terminated. + * Returns {@code true} if this phaser has been terminated. * - * @return {@code true} if this barrier has been terminated + * @return {@code true} if this phaser has been terminated */ public boolean isTerminated() { - return getPhase() < 0; + return root.state < 0L; } /** * Overridable method to perform an action upon impending phase * advance, and to control termination. This method is invoked - * upon arrival of the party tripping the barrier (when all other + * upon arrival of the party advancing this phaser (when all other * waiting parties are dormant). If this method returns {@code - * true}, then, rather than advance the phase number, this barrier - * will be set to a final termination state, and subsequent calls - * to {@link #isTerminated} will return true. Any (unchecked) - * Exception or Error thrown by an invocation of this method is - * propagated to the party attempting to trip the barrier, in - * which case no advance occurs. + * true}, this phaser will be set to a final termination state + * upon advance, and subsequent calls to {@link #isTerminated} + * will return true. Any (unchecked) Exception or Error thrown by + * an invocation of this method is propagated to the party + * attempting to advance this phaser, in which case no advance + * occurs. * *

The arguments to this method provide the state of the phaser - * prevailing for the current transition. (When called from within - * an implementation of {@code onAdvance} the values returned by - * methods such as {@code getPhase} may or may not reliably - * indicate the state to which this transition applies.) + * prevailing for the current transition. The effects of invoking + * arrival, registration, and waiting methods on this phaser from + * within {@code onAdvance} are unspecified and should not be + * relied on. * - *

The default version returns {@code true} when the number of - * registered parties is zero. Normally, overrides that arrange - * termination for other reasons should also preserve this - * property. + *

If this phaser is a member of a tiered set of phasers, then + * {@code onAdvance} is invoked only for its root phaser on each + * advance. * - *

You may override this method to perform an action with side - * effects visible to participating tasks, but it is only sensible - * to do so in designs where all parties register before any - * arrive, and all {@link #awaitAdvance} at each phase. - * Otherwise, you cannot ensure lack of interference from other - * parties during the invocation of this method. Additionally, - * method {@code onAdvance} may be invoked more than once per - * transition if registrations are intermixed with arrivals. + *

To support the most common use cases, the default + * implementation of this method returns {@code true} when the + * number of registered parties has become zero as the result of a + * party invoking {@code arriveAndDeregister}. You can disable + * this behavior, thus enabling continuation upon future + * registrations, by overriding this method to always return + * {@code false}: * - * @param phase the phase number on entering the barrier + *

 {@code
+     * Phaser phaser = new Phaser() {
+     *   protected boolean onAdvance(int phase, int parties) { return false; }
+     * }}
+ * + * @param phase the current phase number on entry to this method, + * before this phaser is advanced * @param registeredParties the current number of registered parties - * @return {@code true} if this barrier should terminate + * @return {@code true} if this phaser should terminate */ protected boolean onAdvance(int phase, int registeredParties) { - return registeredParties <= 0; + return registeredParties == 0; } /** @@ -831,17 +939,138 @@ public class Phaser { * followed by the number of registered parties, and {@code * "arrived = "} followed by the number of arrived parties. * - * @return a string identifying this barrier, as well as its state + * @return a string identifying this phaser, as well as its state */ public String toString() { - long s = getReconciledState(); + return stateToString(reconcileState()); + } + + /** + * Implementation of toString and string-based error messages + */ + private String stateToString(long s) { return super.toString() + "[phase = " + phaseOf(s) + " parties = " + partiesOf(s) + " arrived = " + arrivedOf(s) + "]"; } - // methods for waiting + // Waiting mechanics + + /** + * Removes and signals threads from queue for phase. + */ + private void releaseWaiters(int phase) { + QNode q; // first element of queue + Thread t; // its thread + AtomicReference head = (phase & 1) == 0 ? evenQ : oddQ; + while ((q = head.get()) != null && + q.phase != (int)(root.state >>> PHASE_SHIFT)) { + if (head.compareAndSet(q, q.next) && + (t = q.thread) != null) { + q.thread = null; + LockSupport.unpark(t); + } + } + } + + /** + * Variant of releaseWaiters that additionally tries to remove any + * nodes no longer waiting for advance due to timeout or + * interrupt. Currently, nodes are removed only if they are at + * head of queue, which suffices to reduce memory footprint in + * most usages. + * + * @return current phase on exit + */ + private int abortWait(int phase) { + AtomicReference head = (phase & 1) == 0 ? evenQ : oddQ; + for (;;) { + Thread t; + QNode q = head.get(); + int p = (int)(root.state >>> PHASE_SHIFT); + if (q == null || ((t = q.thread) != null && q.phase == p)) + return p; + if (head.compareAndSet(q, q.next) && t != null) { + q.thread = null; + LockSupport.unpark(t); + } + } + } + + /** The number of CPUs, for spin control */ + private static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** + * The number of times to spin before blocking while waiting for + * advance, per arrival while waiting. On multiprocessors, fully + * blocking and waking up a large number of threads all at once is + * usually a very slow process, so we use rechargeable spins to + * avoid it when threads regularly arrive: When a thread in + * internalAwaitAdvance notices another arrival before blocking, + * and there appear to be enough CPUs available, it spins + * SPINS_PER_ARRIVAL more times before blocking. The value trades + * off good-citizenship vs big unnecessary slowdowns. + */ + static final int SPINS_PER_ARRIVAL = (NCPU < 2) ? 1 : 1 << 8; + + /** + * Possibly blocks and waits for phase to advance unless aborted. + * Call only from root node. + * + * @param phase current phase + * @param node if non-null, the wait node to track interrupt and timeout; + * if null, denotes noninterruptible wait + * @return current phase + */ + private int internalAwaitAdvance(int phase, QNode node) { + releaseWaiters(phase-1); // ensure old queue clean + boolean queued = false; // true when node is enqueued + int lastUnarrived = 0; // to increase spins upon change + int spins = SPINS_PER_ARRIVAL; + long s; + int p; + while ((p = (int)((s = state) >>> PHASE_SHIFT)) == phase) { + if (node == null) { // spinning in noninterruptible mode + int unarrived = (int)s & UNARRIVED_MASK; + if (unarrived != lastUnarrived && + (lastUnarrived = unarrived) < NCPU) + spins += SPINS_PER_ARRIVAL; + boolean interrupted = Thread.interrupted(); + if (interrupted || --spins < 0) { // need node to record intr + node = new QNode(this, phase, false, false, 0L); + node.wasInterrupted = interrupted; + } + } + else if (node.isReleasable()) // done or aborted + break; + else if (!queued) { // push onto queue + AtomicReference head = (phase & 1) == 0 ? evenQ : oddQ; + QNode q = node.next = head.get(); + if ((q == null || q.phase == phase) && + (int)(state >>> PHASE_SHIFT) == phase) // avoid stale enq + queued = head.compareAndSet(q, node); + } + else { + try { + ForkJoinPool.managedBlock(node); + } catch (InterruptedException ie) { + node.wasInterrupted = true; + } + } + } + + if (node != null) { + if (node.thread != null) + node.thread = null; // avoid need for unpark() + if (node.wasInterrupted && !node.interruptible) + Thread.currentThread().interrupt(); + if (p == phase && (p = (int)(state >>> PHASE_SHIFT)) == phase) + return abortWait(phase); // possibly clean up on abort + } + releaseWaiters(phase); + return p; + } /** * Wait nodes for Treiber stack representing wait queue @@ -849,186 +1078,69 @@ public class Phaser { static final class QNode implements ForkJoinPool.ManagedBlocker { final Phaser phaser; final int phase; - final long startTime; - final long nanos; - final boolean timed; final boolean interruptible; - volatile boolean wasInterrupted = false; + final boolean timed; + boolean wasInterrupted; + long nanos; + long lastTime; volatile Thread thread; // nulled to cancel wait QNode next; + QNode(Phaser phaser, int phase, boolean interruptible, - boolean timed, long startTime, long nanos) { + boolean timed, long nanos) { this.phaser = phaser; this.phase = phase; - this.timed = timed; this.interruptible = interruptible; - this.startTime = startTime; this.nanos = nanos; + this.timed = timed; + this.lastTime = timed ? System.nanoTime() : 0L; thread = Thread.currentThread(); } + public boolean isReleasable() { - return (thread == null || - phaser.getPhase() != phase || - (interruptible && wasInterrupted) || - (timed && (nanos - (System.nanoTime() - startTime)) <= 0)); - } - public boolean block() { - if (Thread.interrupted()) { - wasInterrupted = true; - if (interruptible) - return true; - } - if (!timed) - LockSupport.park(this); - else { - long waitTime = nanos - (System.nanoTime() - startTime); - if (waitTime <= 0) - return true; - LockSupport.parkNanos(this, waitTime); - } - return isReleasable(); - } - void signal() { - Thread t = thread; - if (t != null) { + if (thread == null) + return true; + if (phaser.getPhase() != phase) { thread = null; - LockSupport.unpark(t); + return true; } - } - boolean doWait() { - if (thread != null) { - try { - ForkJoinPool.managedBlock(this); - } catch (InterruptedException ie) { + if (Thread.interrupted()) + wasInterrupted = true; + if (wasInterrupted && interruptible) { + thread = null; + return true; + } + if (timed) { + if (nanos > 0L) { + long now = System.nanoTime(); + nanos -= now - lastTime; + lastTime = now; + } + if (nanos <= 0L) { + thread = null; + return true; } } - return wasInterrupted; + return false; } - } - - /** - * Removes and signals waiting threads from wait queue. - */ - private void releaseWaiters(int phase) { - AtomicReference head = queueFor(phase); - QNode q; - while ((q = head.get()) != null) { - if (head.compareAndSet(q, q.next)) - q.signal(); + public boolean block() { + if (isReleasable()) + return true; + else if (!timed) + LockSupport.park(this); + else if (nanos > 0) + LockSupport.parkNanos(this, nanos); + return isReleasable(); } } - /** - * Tries to enqueue given node in the appropriate wait queue. - * - * @return true if successful - */ - private boolean tryEnqueue(QNode node) { - AtomicReference head = queueFor(node.phase); - return head.compareAndSet(node.next = head.get(), node); - } - - /** - * Enqueues node and waits unless aborted or signalled. - * - * @return current phase - */ - private int untimedWait(int phase) { - QNode node = null; - boolean queued = false; - boolean interrupted = false; - int p; - while ((p = getPhase()) == phase) { - if (Thread.interrupted()) - interrupted = true; - else if (node == null) - node = new QNode(this, phase, false, false, 0, 0); - else if (!queued) - queued = tryEnqueue(node); - else - interrupted = node.doWait(); - } - if (node != null) - node.thread = null; - releaseWaiters(phase); - if (interrupted) - Thread.currentThread().interrupt(); - return p; - } - - /** - * Interruptible version - * @return current phase - */ - private int interruptibleWait(int phase) throws InterruptedException { - QNode node = null; - boolean queued = false; - boolean interrupted = false; - int p; - while ((p = getPhase()) == phase && !interrupted) { - if (Thread.interrupted()) - interrupted = true; - else if (node == null) - node = new QNode(this, phase, true, false, 0, 0); - else if (!queued) - queued = tryEnqueue(node); - else - interrupted = node.doWait(); - } - if (node != null) - node.thread = null; - if (p != phase || (p = getPhase()) != phase) - releaseWaiters(phase); - if (interrupted) - throw new InterruptedException(); - return p; - } - - /** - * Timeout version. - * @return current phase - */ - private int timedWait(int phase, long nanos) - throws InterruptedException, TimeoutException { - long startTime = System.nanoTime(); - QNode node = null; - boolean queued = false; - boolean interrupted = false; - int p; - while ((p = getPhase()) == phase && !interrupted) { - if (Thread.interrupted()) - interrupted = true; - else if (nanos - (System.nanoTime() - startTime) <= 0) - break; - else if (node == null) - node = new QNode(this, phase, true, true, startTime, nanos); - else if (!queued) - queued = tryEnqueue(node); - else - interrupted = node.doWait(); - } - if (node != null) - node.thread = null; - if (p != phase || (p = getPhase()) != phase) - releaseWaiters(phase); - if (interrupted) - throw new InterruptedException(); - if (p == phase) - throw new TimeoutException(); - return p; - } - // Unsafe mechanics private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); private static final long stateOffset = objectFieldOffset("state", Phaser.class); - private final boolean casState(long cmp, long val) { - return UNSAFE.compareAndSwapLong(this, stateOffset, cmp, val); - } - private static long objectFieldOffset(String field, Class klazz) { try { return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field)); diff --git a/jdk/src/share/classes/java/util/concurrent/PriorityBlockingQueue.java b/jdk/src/share/classes/java/util/concurrent/PriorityBlockingQueue.java index fec463e3d38..60928ac8a74 100644 --- a/jdk/src/share/classes/java/util/concurrent/PriorityBlockingQueue.java +++ b/jdk/src/share/classes/java/util/concurrent/PriorityBlockingQueue.java @@ -43,11 +43,11 @@ import java.util.*; * the same ordering rules as class {@link PriorityQueue} and supplies * blocking retrieval operations. While this queue is logically * unbounded, attempted additions may fail due to resource exhaustion - * (causing OutOfMemoryError). This class does not permit - * null elements. A priority queue relying on {@linkplain + * (causing {@code OutOfMemoryError}). This class does not permit + * {@code null} elements. A priority queue relying on {@linkplain * Comparable natural ordering} also does not permit insertion of * non-comparable objects (doing so results in - * ClassCastException). + * {@code ClassCastException}). * *

This class and its iterator implement all of the * optional methods of the {@link Collection} and {@link @@ -55,7 +55,7 @@ import java.util.*; * #iterator()} is not guaranteed to traverse the elements of * the PriorityBlockingQueue in any particular order. If you need * ordered traversal, consider using - * Arrays.sort(pq.toArray()). Also, method drainTo + * {@code Arrays.sort(pq.toArray())}. Also, method {@code drainTo} * can be used to remove some or all elements in priority * order and place them in another collection. * @@ -65,12 +65,12 @@ import java.util.*; * secondary key to break ties in primary priority values. For * example, here is a class that applies first-in-first-out * tie-breaking to comparable elements. To use it, you would insert a - * new FIFOEntry(anEntry) instead of a plain entry object. + * {@code new FIFOEntry(anEntry)} instead of a plain entry object. * - *

- * class FIFOEntry<E extends Comparable<? super E>>
- *     implements Comparable<FIFOEntry<E>> {
- *   final static AtomicLong seq = new AtomicLong();
+ *  
 {@code
+ * class FIFOEntry>
+ *     implements Comparable> {
+ *   static final AtomicLong seq = new AtomicLong(0);
  *   final long seqNum;
  *   final E entry;
  *   public FIFOEntry(E entry) {
@@ -78,13 +78,13 @@ import java.util.*;
  *     this.entry = entry;
  *   }
  *   public E getEntry() { return entry; }
- *   public int compareTo(FIFOEntry<E> other) {
+ *   public int compareTo(FIFOEntry other) {
  *     int res = entry.compareTo(other.entry);
- *     if (res == 0 && other.entry != this.entry)
- *       res = (seqNum < other.seqNum ? -1 : 1);
+ *     if (res == 0 && other.entry != this.entry)
+ *       res = (seqNum < other.seqNum ? -1 : 1);
  *     return res;
  *   }
- * }
+ * }}
* *

This class is a member of the * @@ -98,34 +98,102 @@ public class PriorityBlockingQueue extends AbstractQueue implements BlockingQueue, java.io.Serializable { private static final long serialVersionUID = 5595510919245408276L; - private final PriorityQueue q; - private final ReentrantLock lock = new ReentrantLock(true); - private final Condition notEmpty = lock.newCondition(); + /* + * The implementation uses an array-based binary heap, with public + * operations protected with a single lock. However, allocation + * during resizing uses a simple spinlock (used only while not + * holding main lock) in order to allow takes to operate + * concurrently with allocation. This avoids repeated + * postponement of waiting consumers and consequent element + * build-up. The need to back away from lock during allocation + * makes it impossible to simply wrap delegated + * java.util.PriorityQueue operations within a lock, as was done + * in a previous version of this class. To maintain + * interoperability, a plain PriorityQueue is still used during + * serialization, which maintains compatibility at the espense of + * transiently doubling overhead. + */ /** - * Creates a PriorityBlockingQueue with the default + * Default array capacity. + */ + private static final int DEFAULT_INITIAL_CAPACITY = 11; + + /** + * The maximum size of array to allocate. + * Some VMs reserve some header words in an array. + * Attempts to allocate larger arrays may result in + * OutOfMemoryError: Requested array size exceeds VM limit + */ + private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + /** + * Priority queue represented as a balanced binary heap: the two + * children of queue[n] are queue[2*n+1] and queue[2*(n+1)]. The + * priority queue is ordered by comparator, or by the elements' + * natural ordering, if comparator is null: For each node n in the + * heap and each descendant d of n, n <= d. The element with the + * lowest value is in queue[0], assuming the queue is nonempty. + */ + private transient Object[] queue; + + /** + * The number of elements in the priority queue. + */ + private transient int size; + + /** + * The comparator, or null if priority queue uses elements' + * natural ordering. + */ + private transient Comparator comparator; + + /** + * Lock used for all public operations + */ + private final ReentrantLock lock; + + /** + * Condition for blocking when empty + */ + private final Condition notEmpty; + + /** + * Spinlock for allocation, acquired via CAS. + */ + private transient volatile int allocationSpinLock; + + /** + * A plain PriorityQueue used only for serialization, + * to maintain compatibility with previous versions + * of this class. Non-null only during serialization/deserialization. + */ + private PriorityQueue q; + + /** + * Creates a {@code PriorityBlockingQueue} with the default * initial capacity (11) that orders its elements according to * their {@linkplain Comparable natural ordering}. */ public PriorityBlockingQueue() { - q = new PriorityQueue(); + this(DEFAULT_INITIAL_CAPACITY, null); } /** - * Creates a PriorityBlockingQueue with the specified + * Creates a {@code PriorityBlockingQueue} with the specified * initial capacity that orders its elements according to their * {@linkplain Comparable natural ordering}. * * @param initialCapacity the initial capacity for this priority queue - * @throws IllegalArgumentException if initialCapacity is less + * @throws IllegalArgumentException if {@code initialCapacity} is less * than 1 */ public PriorityBlockingQueue(int initialCapacity) { - q = new PriorityQueue(initialCapacity, null); + this(initialCapacity, null); } /** - * Creates a PriorityBlockingQueue with the specified initial + * Creates a {@code PriorityBlockingQueue} with the specified initial * capacity that orders its elements according to the specified * comparator. * @@ -133,16 +201,21 @@ public class PriorityBlockingQueue extends AbstractQueue * @param comparator the comparator that will be used to order this * priority queue. If {@code null}, the {@linkplain Comparable * natural ordering} of the elements will be used. - * @throws IllegalArgumentException if initialCapacity is less + * @throws IllegalArgumentException if {@code initialCapacity} is less * than 1 */ public PriorityBlockingQueue(int initialCapacity, Comparator comparator) { - q = new PriorityQueue(initialCapacity, comparator); + if (initialCapacity < 1) + throw new IllegalArgumentException(); + this.lock = new ReentrantLock(); + this.notEmpty = lock.newCondition(); + this.comparator = comparator; + this.queue = new Object[initialCapacity]; } /** - * Creates a PriorityBlockingQueue containing the elements + * Creates a {@code PriorityBlockingQueue} containing the elements * in the specified collection. If the specified collection is a * {@link SortedSet} or a {@link PriorityQueue}, this * priority queue will be ordered according to the same ordering. @@ -158,14 +231,215 @@ public class PriorityBlockingQueue extends AbstractQueue * of its elements are null */ public PriorityBlockingQueue(Collection c) { - q = new PriorityQueue(c); + this.lock = new ReentrantLock(); + this.notEmpty = lock.newCondition(); + boolean heapify = true; // true if not known to be in heap order + boolean screen = true; // true if must screen for nulls + if (c instanceof SortedSet) { + SortedSet ss = (SortedSet) c; + this.comparator = (Comparator) ss.comparator(); + heapify = false; + } + else if (c instanceof PriorityBlockingQueue) { + PriorityBlockingQueue pq = + (PriorityBlockingQueue) c; + this.comparator = (Comparator) pq.comparator(); + screen = false; + if (pq.getClass() == PriorityBlockingQueue.class) // exact match + heapify = false; + } + Object[] a = c.toArray(); + int n = a.length; + // If c.toArray incorrectly doesn't return Object[], copy it. + if (a.getClass() != Object[].class) + a = Arrays.copyOf(a, n, Object[].class); + if (screen && (n == 1 || this.comparator != null)) { + for (int i = 0; i < n; ++i) + if (a[i] == null) + throw new NullPointerException(); + } + this.queue = a; + this.size = n; + if (heapify) + heapify(); + } + + /** + * Tries to grow array to accommodate at least one more element + * (but normally expand by about 50%), giving up (allowing retry) + * on contention (which we expect to be rare). Call only while + * holding lock. + * + * @param array the heap array + * @param oldCap the length of the array + */ + private void tryGrow(Object[] array, int oldCap) { + lock.unlock(); // must release and then re-acquire main lock + Object[] newArray = null; + if (allocationSpinLock == 0 && + UNSAFE.compareAndSwapInt(this, allocationSpinLockOffset, + 0, 1)) { + try { + int newCap = oldCap + ((oldCap < 64) ? + (oldCap + 2) : // grow faster if small + (oldCap >> 1)); + if (newCap - MAX_ARRAY_SIZE > 0) { // possible overflow + int minCap = oldCap + 1; + if (minCap < 0 || minCap > MAX_ARRAY_SIZE) + throw new OutOfMemoryError(); + newCap = MAX_ARRAY_SIZE; + } + if (newCap > oldCap && queue == array) + newArray = new Object[newCap]; + } finally { + allocationSpinLock = 0; + } + } + if (newArray == null) // back off if another thread is allocating + Thread.yield(); + lock.lock(); + if (newArray != null && queue == array) { + queue = newArray; + System.arraycopy(array, 0, newArray, 0, oldCap); + } + } + + /** + * Mechanics for poll(). Call only while holding lock. + */ + private E extract() { + E result; + int n = size - 1; + if (n < 0) + result = null; + else { + Object[] array = queue; + result = (E) array[0]; + E x = (E) array[n]; + array[n] = null; + Comparator cmp = comparator; + if (cmp == null) + siftDownComparable(0, x, array, n); + else + siftDownUsingComparator(0, x, array, n, cmp); + size = n; + } + return result; + } + + /** + * Inserts item x at position k, maintaining heap invariant by + * promoting x up the tree until it is greater than or equal to + * its parent, or is the root. + * + * To simplify and speed up coercions and comparisons. the + * Comparable and Comparator versions are separated into different + * methods that are otherwise identical. (Similarly for siftDown.) + * These methods are static, with heap state as arguments, to + * simplify use in light of possible comparator exceptions. + * + * @param k the position to fill + * @param x the item to insert + * @param array the heap array + * @param n heap size + */ + private static void siftUpComparable(int k, T x, Object[] array) { + Comparable key = (Comparable) x; + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = array[parent]; + if (key.compareTo((T) e) >= 0) + break; + array[k] = e; + k = parent; + } + array[k] = key; + } + + private static void siftUpUsingComparator(int k, T x, Object[] array, + Comparator cmp) { + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = array[parent]; + if (cmp.compare(x, (T) e) >= 0) + break; + array[k] = e; + k = parent; + } + array[k] = x; + } + + /** + * Inserts item x at position k, maintaining heap invariant by + * demoting x down the tree repeatedly until it is less than or + * equal to its children or is a leaf. + * + * @param k the position to fill + * @param x the item to insert + * @param array the heap array + * @param n heap size + */ + private static void siftDownComparable(int k, T x, Object[] array, + int n) { + Comparable key = (Comparable)x; + int half = n >>> 1; // loop while a non-leaf + while (k < half) { + int child = (k << 1) + 1; // assume left child is least + Object c = array[child]; + int right = child + 1; + if (right < n && + ((Comparable) c).compareTo((T) array[right]) > 0) + c = array[child = right]; + if (key.compareTo((T) c) <= 0) + break; + array[k] = c; + k = child; + } + array[k] = key; + } + + private static void siftDownUsingComparator(int k, T x, Object[] array, + int n, + Comparator cmp) { + int half = n >>> 1; + while (k < half) { + int child = (k << 1) + 1; + Object c = array[child]; + int right = child + 1; + if (right < n && cmp.compare((T) c, (T) array[right]) > 0) + c = array[child = right]; + if (cmp.compare(x, (T) c) <= 0) + break; + array[k] = c; + k = child; + } + array[k] = x; + } + + /** + * Establishes the heap invariant (described above) in the entire tree, + * assuming nothing about the order of the elements prior to the call. + */ + private void heapify() { + Object[] array = queue; + int n = size; + int half = (n >>> 1) - 1; + Comparator cmp = comparator; + if (cmp == null) { + for (int i = half; i >= 0; i--) + siftDownComparable(i, (E) array[i], array, n); + } + else { + for (int i = half; i >= 0; i--) + siftDownUsingComparator(i, (E) array[i], array, n, cmp); + } } /** * Inserts the specified element into this priority queue. * * @param e the element to add - * @return true (as specified by {@link Collection#add}) + * @return {@code true} (as specified by {@link Collection#add}) * @throws ClassCastException if the specified element cannot be compared * with elements currently in the priority queue according to the * priority queue's ordering @@ -177,30 +451,41 @@ public class PriorityBlockingQueue extends AbstractQueue /** * Inserts the specified element into this priority queue. + * As the queue is unbounded, this method will never return {@code false}. * * @param e the element to add - * @return true (as specified by {@link Queue#offer}) + * @return {@code true} (as specified by {@link Queue#offer}) * @throws ClassCastException if the specified element cannot be compared * with elements currently in the priority queue according to the * priority queue's ordering * @throws NullPointerException if the specified element is null */ public boolean offer(E e) { + if (e == null) + throw new NullPointerException(); final ReentrantLock lock = this.lock; lock.lock(); + int n, cap; + Object[] array; + while ((n = size) >= (cap = (array = queue).length)) + tryGrow(array, cap); try { - boolean ok = q.offer(e); - assert ok; + Comparator cmp = comparator; + if (cmp == null) + siftUpComparable(n, e, array); + else + siftUpUsingComparator(n, e, array, cmp); + size = n + 1; notEmpty.signal(); - return true; } finally { lock.unlock(); } + return true; } /** - * Inserts the specified element into this priority queue. As the queue is - * unbounded this method will never block. + * Inserts the specified element into this priority queue. + * As the queue is unbounded, this method will never block. * * @param e the element to add * @throws ClassCastException if the specified element cannot be compared @@ -213,13 +498,15 @@ public class PriorityBlockingQueue extends AbstractQueue } /** - * Inserts the specified element into this priority queue. As the queue is - * unbounded this method will never block. + * Inserts the specified element into this priority queue. + * As the queue is unbounded, this method will never block or + * return {@code false}. * * @param e the element to add * @param timeout This parameter is ignored as the method never blocks * @param unit This parameter is ignored as the method never blocks - * @return true + * @return {@code true} (as specified by + * {@link BlockingQueue#offer(Object,long,TimeUnit) BlockingQueue.offer}) * @throws ClassCastException if the specified element cannot be compared * with elements currently in the priority queue according to the * priority queue's ordering @@ -232,97 +519,123 @@ public class PriorityBlockingQueue extends AbstractQueue public E poll() { final ReentrantLock lock = this.lock; lock.lock(); + E result; try { - return q.poll(); + result = extract(); } finally { lock.unlock(); } + return result; } public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); + E result; try { - try { - while (q.size() == 0) - notEmpty.await(); - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to non-interrupted thread - throw ie; - } - E x = q.poll(); - assert x != null; - return x; + while ( (result = extract()) == null) + notEmpty.await(); } finally { lock.unlock(); } + return result; } public E poll(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); + E result; try { - for (;;) { - E x = q.poll(); - if (x != null) - return x; - if (nanos <= 0) - return null; - try { - nanos = notEmpty.awaitNanos(nanos); - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to non-interrupted thread - throw ie; - } - } + while ( (result = extract()) == null && nanos > 0) + nanos = notEmpty.awaitNanos(nanos); } finally { lock.unlock(); } + return result; } public E peek() { final ReentrantLock lock = this.lock; lock.lock(); + E result; try { - return q.peek(); + result = size > 0 ? (E) queue[0] : null; } finally { lock.unlock(); } + return result; } /** * Returns the comparator used to order the elements in this queue, - * or null if this queue uses the {@linkplain Comparable + * or {@code null} if this queue uses the {@linkplain Comparable * natural ordering} of its elements. * * @return the comparator used to order the elements in this queue, - * or null if this queue uses the natural + * or {@code null} if this queue uses the natural * ordering of its elements */ public Comparator comparator() { - return q.comparator(); + return comparator; } public int size() { final ReentrantLock lock = this.lock; lock.lock(); try { - return q.size(); + return size; } finally { lock.unlock(); } } /** - * Always returns Integer.MAX_VALUE because - * a PriorityBlockingQueue is not capacity constrained. - * @return Integer.MAX_VALUE + * Always returns {@code Integer.MAX_VALUE} because + * a {@code PriorityBlockingQueue} is not capacity constrained. + * @return {@code Integer.MAX_VALUE} always */ public int remainingCapacity() { return Integer.MAX_VALUE; } + private int indexOf(Object o) { + if (o != null) { + Object[] array = queue; + int n = size; + for (int i = 0; i < n; i++) + if (o.equals(array[i])) + return i; + } + return -1; + } + + /** + * Removes the ith element from queue. + */ + private void removeAt(int i) { + Object[] array = queue; + int n = size - 1; + if (n == i) // removed last element + array[i] = null; + else { + E moved = (E) array[n]; + array[n] = null; + Comparator cmp = comparator; + if (cmp == null) + siftDownComparable(i, moved, array, n); + else + siftDownUsingComparator(i, moved, array, n, cmp); + if (array[i] == moved) { + if (cmp == null) + siftUpComparable(i, moved, array); + else + siftUpUsingComparator(i, moved, array, cmp); + } + } + size = n; + } + /** * Removes a single instance of the specified element from this queue, * if it is present. More formally, removes an element {@code e} such @@ -332,13 +645,40 @@ public class PriorityBlockingQueue extends AbstractQueue * result of the call). * * @param o element to be removed from this queue, if present - * @return true 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) { + boolean removed = false; final ReentrantLock lock = this.lock; lock.lock(); try { - return q.remove(o); + int i = indexOf(o); + if (i != -1) { + removeAt(i); + removed = true; + } + } finally { + lock.unlock(); + } + return removed; + } + + + /** + * Identity-based version for use in Itr.remove + */ + private void removeEQ(Object o) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] array = queue; + int n = size; + for (int i = 0; i < n; i++) { + if (o == array[i]) { + removeAt(i); + break; + } + } } finally { lock.unlock(); } @@ -350,16 +690,18 @@ public class PriorityBlockingQueue extends AbstractQueue * at least one element {@code e} such that {@code o.equals(e)}. * * @param o object to be checked for containment in this queue - * @return true if this queue contains the specified element + * @return {@code true} if this queue contains the specified element */ public boolean contains(Object o) { + int index; final ReentrantLock lock = this.lock; lock.lock(); try { - return q.contains(o); + index = indexOf(o); } finally { lock.unlock(); } + return index != -1; } /** @@ -379,7 +721,7 @@ public class PriorityBlockingQueue extends AbstractQueue final ReentrantLock lock = this.lock; lock.lock(); try { - return q.toArray(); + return Arrays.copyOf(queue, size); } finally { lock.unlock(); } @@ -390,7 +732,18 @@ public class PriorityBlockingQueue extends AbstractQueue final ReentrantLock lock = this.lock; lock.lock(); try { - return q.toString(); + int n = size; + if (n == 0) + return "[]"; + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (int i = 0; i < n; ++i) { + E e = (E)queue[i]; + sb.append(e == this ? "(this Collection)" : e); + if (i != n - 1) + sb.append(',').append(' '); + } + return sb.append(']').toString(); } finally { lock.unlock(); } @@ -412,7 +765,7 @@ public class PriorityBlockingQueue extends AbstractQueue try { int n = 0; E e; - while ( (e = q.poll()) != null) { + while ( (e = extract()) != null) { c.add(e); ++n; } @@ -440,7 +793,7 @@ public class PriorityBlockingQueue extends AbstractQueue try { int n = 0; E e; - while (n < maxElements && (e = q.poll()) != null) { + while (n < maxElements && (e = extract()) != null) { c.add(e); ++n; } @@ -458,7 +811,11 @@ public class PriorityBlockingQueue extends AbstractQueue final ReentrantLock lock = this.lock; lock.lock(); try { - q.clear(); + Object[] array = queue; + int n = size; + size = 0; + for (int i = 0; i < n; i++) + array[i] = null; } finally { lock.unlock(); } @@ -475,22 +832,22 @@ public class PriorityBlockingQueue extends AbstractQueue *

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 - * null. + * {@code null}. * *

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. * - *

Suppose x is a queue known to contain only strings. + *

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 String: + * allocated array of {@code String}: * *

      *     String[] y = x.toArray(new String[0]);
* - * Note that toArray(new Object[0]) is identical in function to - * toArray(). + * 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 @@ -505,7 +862,14 @@ public class PriorityBlockingQueue extends AbstractQueue final ReentrantLock lock = this.lock; lock.lock(); try { - return q.toArray(a); + int n = size; + if (a.length < n) + // Make a new array of a's runtime type, but my contents: + return (T[]) Arrays.copyOf(queue, size, a.getClass()); + System.arraycopy(queue, 0, a, 0, n); + if (a.length > n) + a[n] = null; + return a; } finally { lock.unlock(); } @@ -514,8 +878,9 @@ public class PriorityBlockingQueue extends AbstractQueue /** * Returns an iterator over the elements in this queue. The * iterator does not return the elements in any particular order. - * The returned Iterator is a "weakly consistent" - * iterator that will never throw {@link + * + *

The returned 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 @@ -530,7 +895,7 @@ public class PriorityBlockingQueue extends AbstractQueue /** * Snapshot iterator that works off copy of underlying q array. */ - private class Itr implements Iterator { + final class Itr implements Iterator { final Object[] array; // Array of all elements int cursor; // index of next element to return; int lastRet; // index of last element, or -1 if no such @@ -554,39 +919,65 @@ public class PriorityBlockingQueue extends AbstractQueue public void remove() { if (lastRet < 0) throw new IllegalStateException(); - Object x = array[lastRet]; + removeEQ(array[lastRet]); lastRet = -1; - // Traverse underlying queue to find == element, - // not just a .equals element. - lock.lock(); - try { - for (Iterator it = q.iterator(); it.hasNext(); ) { - if (it.next() == x) { - it.remove(); - return; - } - } - } finally { - lock.unlock(); - } } } /** - * Saves the state to a stream (that is, serializes it). This - * merely wraps default serialization within lock. The - * serialization strategy for items is left to underlying - * Queue. Note that locking is not needed on deserialization, so - * readObject is not defined, just relying on default. + * Saves the state to a stream (that is, serializes it). For + * compatibility with previous version of this class, + * elements are first copied to a java.util.PriorityQueue, + * which is then serialized. */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { lock.lock(); try { + int n = size; // avoid zero capacity argument + q = new PriorityQueue(n == 0 ? 1 : n, comparator); + q.addAll(this); s.defaultWriteObject(); } finally { + q = null; lock.unlock(); } } + /** + * Reconstitutes the {@code PriorityBlockingQueue} instance from a stream + * (that is, deserializes it). + * + * @param s the stream + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + try { + s.defaultReadObject(); + this.queue = new Object[q.size()]; + comparator = q.comparator(); + addAll(q); + } finally { + q = null; + } + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); + private static final long allocationSpinLockOffset = + objectFieldOffset(UNSAFE, "allocationSpinLock", + PriorityBlockingQueue.class); + + 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/jdk/src/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java b/jdk/src/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java index 46961b7aa40..f1731420897 100644 --- a/jdk/src/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java +++ b/jdk/src/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java @@ -360,8 +360,12 @@ public class ScheduledThreadPoolExecutor getExecuteExistingDelayedTasksAfterShutdownPolicy(); boolean keepPeriodic = getContinueExistingPeriodicTasksAfterShutdownPolicy(); - if (!keepDelayed && !keepPeriodic) + if (!keepDelayed && !keepPeriodic) { + for (Object e : q.toArray()) + if (e instanceof RunnableScheduledFuture) + ((RunnableScheduledFuture) e).cancel(false); q.clear(); + } else { // Traverse snapshot to avoid iterator exceptions for (Object e : q.toArray()) { diff --git a/jdk/src/share/classes/java/util/concurrent/SynchronousQueue.java b/jdk/src/share/classes/java/util/concurrent/SynchronousQueue.java index f39f1a906e6..47b352f9e35 100644 --- a/jdk/src/share/classes/java/util/concurrent/SynchronousQueue.java +++ b/jdk/src/share/classes/java/util/concurrent/SynchronousQueue.java @@ -163,7 +163,7 @@ public class SynchronousQueue extends AbstractQueue /** * Shared internal API for dual stacks and queues. */ - static abstract class Transferer { + abstract static class Transferer { /** * Performs a put or take. * @@ -190,7 +190,7 @@ public class SynchronousQueue extends AbstractQueue * seems not to vary with number of CPUs (beyond 2) so is just * a constant. */ - static final int maxTimedSpins = (NCPUS < 2)? 0 : 32; + static final int maxTimedSpins = (NCPUS < 2) ? 0 : 32; /** * The number of times to spin before blocking in untimed waits. @@ -241,19 +241,11 @@ public class SynchronousQueue extends AbstractQueue this.item = item; } - static final AtomicReferenceFieldUpdater - nextUpdater = AtomicReferenceFieldUpdater.newUpdater - (SNode.class, SNode.class, "next"); - boolean casNext(SNode cmp, SNode val) { - return (cmp == next && - nextUpdater.compareAndSet(this, cmp, val)); + return cmp == next && + UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); } - static final AtomicReferenceFieldUpdater - matchUpdater = AtomicReferenceFieldUpdater.newUpdater - (SNode.class, SNode.class, "match"); - /** * Tries to match node s to this node, if so, waking up thread. * Fulfillers call tryMatch to identify their waiters. @@ -264,7 +256,7 @@ public class SynchronousQueue extends AbstractQueue */ boolean tryMatch(SNode s) { if (match == null && - matchUpdater.compareAndSet(this, null, s)) { + UNSAFE.compareAndSwapObject(this, matchOffset, null, s)) { Thread w = waiter; if (w != null) { // waiters need at most one unpark waiter = null; @@ -279,23 +271,28 @@ public class SynchronousQueue extends AbstractQueue * Tries to cancel a wait by matching node to itself. */ void tryCancel() { - matchUpdater.compareAndSet(this, null, this); + UNSAFE.compareAndSwapObject(this, matchOffset, null, this); } boolean isCancelled() { return match == this; } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); + private static final long nextOffset = + objectFieldOffset(UNSAFE, "next", SNode.class); + private static final long matchOffset = + objectFieldOffset(UNSAFE, "match", SNode.class); + } /** The head (top) of the stack */ volatile SNode head; - static final AtomicReferenceFieldUpdater - headUpdater = AtomicReferenceFieldUpdater.newUpdater - (TransferStack.class, SNode.class, "head"); - boolean casHead(SNode h, SNode nh) { - return h == head && headUpdater.compareAndSet(this, h, nh); + return h == head && + UNSAFE.compareAndSwapObject(this, headOffset, h, nh); } /** @@ -338,7 +335,7 @@ public class SynchronousQueue extends AbstractQueue */ SNode s = null; // constructed/reused as needed - int mode = (e == null)? REQUEST : DATA; + int mode = (e == null) ? REQUEST : DATA; for (;;) { SNode h = head; @@ -356,7 +353,7 @@ public class SynchronousQueue extends AbstractQueue } if ((h = head) != null && h.next == s) casHead(h, s.next); // help s's fulfiller - return mode == REQUEST? m.item : s.item; + return (mode == REQUEST) ? m.item : s.item; } } else if (!isFulfilling(h.mode)) { // try to fulfill if (h.isCancelled()) // already cancelled @@ -372,7 +369,7 @@ public class SynchronousQueue extends AbstractQueue SNode mn = m.next; if (m.tryMatch(s)) { casHead(s, mn); // pop both s and m - return (mode == REQUEST)? m.item : s.item; + return (mode == REQUEST) ? m.item : s.item; } else // lost match s.casNext(m, mn); // help unlink } @@ -423,11 +420,11 @@ public class SynchronousQueue extends AbstractQueue * and don't wait at all, so are trapped in transfer * method rather than calling awaitFulfill. */ - long lastTime = (timed)? System.nanoTime() : 0; + long lastTime = timed ? System.nanoTime() : 0; Thread w = Thread.currentThread(); SNode h = head; - int spins = (shouldSpin(s)? - (timed? maxTimedSpins : maxUntimedSpins) : 0); + int spins = (shouldSpin(s) ? + (timed ? maxTimedSpins : maxUntimedSpins) : 0); for (;;) { if (w.isInterrupted()) s.tryCancel(); @@ -444,7 +441,7 @@ public class SynchronousQueue extends AbstractQueue } } if (spins > 0) - spins = shouldSpin(s)? (spins-1) : 0; + spins = shouldSpin(s) ? (spins-1) : 0; else if (s.waiter == null) s.waiter = w; // establish waiter so can park next iter else if (!timed) @@ -499,6 +496,12 @@ public class SynchronousQueue extends AbstractQueue p = n; } } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); + private static final long headOffset = + objectFieldOffset(UNSAFE, "head", TransferStack.class); + } /** Dual Queue */ @@ -524,29 +527,21 @@ public class SynchronousQueue extends AbstractQueue this.isData = isData; } - static final AtomicReferenceFieldUpdater - nextUpdater = AtomicReferenceFieldUpdater.newUpdater - (QNode.class, QNode.class, "next"); - boolean casNext(QNode cmp, QNode val) { - return (next == cmp && - nextUpdater.compareAndSet(this, cmp, val)); + return next == cmp && + UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); } - static final AtomicReferenceFieldUpdater - itemUpdater = AtomicReferenceFieldUpdater.newUpdater - (QNode.class, Object.class, "item"); - boolean casItem(Object cmp, Object val) { - return (item == cmp && - itemUpdater.compareAndSet(this, cmp, val)); + return item == cmp && + UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); } /** * Tries to cancel by CAS'ing ref to this as item. */ void tryCancel(Object cmp) { - itemUpdater.compareAndSet(this, cmp, this); + UNSAFE.compareAndSwapObject(this, itemOffset, cmp, this); } boolean isCancelled() { @@ -561,6 +556,13 @@ public class SynchronousQueue extends AbstractQueue boolean isOffList() { return next == this; } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); + private static final long nextOffset = + objectFieldOffset(UNSAFE, "next", QNode.class); + private static final long itemOffset = + objectFieldOffset(UNSAFE, "item", QNode.class); } /** Head of queue */ @@ -580,41 +582,30 @@ public class SynchronousQueue extends AbstractQueue tail = h; } - static final AtomicReferenceFieldUpdater - headUpdater = AtomicReferenceFieldUpdater.newUpdater - (TransferQueue.class, QNode.class, "head"); - /** * Tries to cas nh as new head; if successful, unlink * old head's next node to avoid garbage retention. */ void advanceHead(QNode h, QNode nh) { - if (h == head && headUpdater.compareAndSet(this, h, nh)) + if (h == head && + UNSAFE.compareAndSwapObject(this, headOffset, h, nh)) h.next = h; // forget old next } - static final AtomicReferenceFieldUpdater - tailUpdater = AtomicReferenceFieldUpdater.newUpdater - (TransferQueue.class, QNode.class, "tail"); - /** * Tries to cas nt as new tail. */ void advanceTail(QNode t, QNode nt) { if (tail == t) - tailUpdater.compareAndSet(this, t, nt); + UNSAFE.compareAndSwapObject(this, tailOffset, t, nt); } - static final AtomicReferenceFieldUpdater - cleanMeUpdater = AtomicReferenceFieldUpdater.newUpdater - (TransferQueue.class, QNode.class, "cleanMe"); - /** * Tries to CAS cleanMe slot. */ boolean casCleanMe(QNode cmp, QNode val) { - return (cleanMe == cmp && - cleanMeUpdater.compareAndSet(this, cmp, val)); + return cleanMe == cmp && + UNSAFE.compareAndSwapObject(this, cleanMeOffset, cmp, val); } /** @@ -683,7 +674,7 @@ public class SynchronousQueue extends AbstractQueue s.item = s; s.waiter = null; } - return (x != null)? x : e; + return (x != null) ? x : e; } else { // complementary-mode QNode m = h.next; // node to fulfill @@ -700,7 +691,7 @@ public class SynchronousQueue extends AbstractQueue advanceHead(h, m); // successfully fulfilled LockSupport.unpark(m.waiter); - return (x != null)? x : e; + return (x != null) ? x : e; } } } @@ -716,10 +707,10 @@ public class SynchronousQueue extends AbstractQueue */ Object awaitFulfill(QNode s, Object e, boolean timed, long nanos) { /* Same idea as TransferStack.awaitFulfill */ - long lastTime = (timed)? System.nanoTime() : 0; + long lastTime = timed ? System.nanoTime() : 0; Thread w = Thread.currentThread(); int spins = ((head.next == s) ? - (timed? maxTimedSpins : maxUntimedSpins) : 0); + (timed ? maxTimedSpins : maxUntimedSpins) : 0); for (;;) { if (w.isInterrupted()) s.tryCancel(e); @@ -799,6 +790,16 @@ public class SynchronousQueue extends AbstractQueue return; // Postpone cleaning s } } + + // unsafe mechanics + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); + private static final long headOffset = + objectFieldOffset(UNSAFE, "head", TransferQueue.class); + private static final long tailOffset = + objectFieldOffset(UNSAFE, "tail", TransferQueue.class); + private static final long cleanMeOffset = + objectFieldOffset(UNSAFE, "cleanMe", TransferQueue.class); + } /** @@ -824,7 +825,7 @@ public class SynchronousQueue extends AbstractQueue * access; otherwise the order is unspecified. */ public SynchronousQueue(boolean fair) { - transferer = (fair)? new TransferQueue() : new TransferStack(); + transferer = fair ? new TransferQueue() : new TransferStack(); } /** @@ -1141,4 +1142,17 @@ public class SynchronousQueue extends AbstractQueue transferer = new TransferStack(); } + // Unsafe mechanics + 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/jdk/src/share/classes/java/util/concurrent/ThreadPoolExecutor.java b/jdk/src/share/classes/java/util/concurrent/ThreadPoolExecutor.java index bfd3b8381f8..841a5804430 100644 --- a/jdk/src/share/classes/java/util/concurrent/ThreadPoolExecutor.java +++ b/jdk/src/share/classes/java/util/concurrent/ThreadPoolExecutor.java @@ -1841,6 +1841,43 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } } + /** + * Returns a string identifying this pool, as well as its state, + * including indications of run state and estimated worker and + * task counts. + * + * @return a string identifying this pool, as well as its state + */ + public String toString() { + long ncompleted; + int nworkers, nactive; + final ReentrantLock mainLock = this.mainLock; + mainLock.lock(); + try { + ncompleted = completedTaskCount; + nactive = 0; + nworkers = workers.size(); + for (Worker w : workers) { + ncompleted += w.completedTasks; + if (w.isLocked()) + ++nactive; + } + } finally { + mainLock.unlock(); + } + int c = ctl.get(); + String rs = (runStateLessThan(c, SHUTDOWN) ? "Running" : + (runStateAtLeast(c, TERMINATED) ? "Terminated" : + "Shutting down")); + return super.toString() + + "[" + rs + + ", pool size = " + nworkers + + ", active threads = " + nactive + + ", queued tasks = " + workQueue.size() + + ", completed tasks = " + ncompleted + + "]"; + } + /* Extension hooks */ /** @@ -1961,7 +1998,9 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * @throws RejectedExecutionException always. */ public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { - throw new RejectedExecutionException(); + throw new RejectedExecutionException("Task " + r.toString() + + " rejected from " + + e.toString()); } } diff --git a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java index 2a9b8a80db0..54b9488ab51 100644 --- a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java +++ b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java @@ -48,28 +48,37 @@ import java.util.*; public class AtomicIntegerArray implements java.io.Serializable { private static final long serialVersionUID = 2862133569453604235L; - // setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final int base = unsafe.arrayBaseOffset(int[].class); - private static final int scale = unsafe.arrayIndexScale(int[].class); + private static final int shift; private final int[] array; - private long rawIndex(int i) { + static { + int scale = unsafe.arrayIndexScale(int[].class); + if ((scale & (scale - 1)) != 0) + throw new Error("data type scale not a power of two"); + shift = 31 - Integer.numberOfLeadingZeros(scale); + } + + private long checkedByteOffset(int i) { if (i < 0 || i >= array.length) throw new IndexOutOfBoundsException("index " + i); - return base + (long) i * scale; + + return byteOffset(i); + } + + private static long byteOffset(int i) { + return ((long) i << shift) + base; } /** - * 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); } /** @@ -80,17 +89,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(); } /** @@ -109,7 +109,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); } /** @@ -119,7 +123,7 @@ 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); } /** @@ -130,7 +134,7 @@ public class AtomicIntegerArray implements java.io.Serializable { * @since 1.6 */ public final void lazySet(int i, int newValue) { - unsafe.putOrderedInt(array, rawIndex(i), newValue); + unsafe.putOrderedInt(array, checkedByteOffset(i), newValue); } /** @@ -142,9 +146,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; } } @@ -160,8 +165,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); } /** @@ -188,12 +196,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); } /** @@ -203,12 +206,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); } /** @@ -219,10 +217,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; } } @@ -234,12 +232,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); } /** @@ -249,12 +242,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); } /** @@ -265,22 +253,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(',').append(' '); + } } } diff --git a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLongArray.java b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLongArray.java index d1f404ca69b..f1873c54d7a 100644 --- a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLongArray.java +++ b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLongArray.java @@ -47,28 +47,37 @@ import java.util.*; public class AtomicLongArray implements java.io.Serializable { private static final long serialVersionUID = -2308431214976778248L; - // setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final int base = unsafe.arrayBaseOffset(long[].class); - private static final int scale = unsafe.arrayIndexScale(long[].class); + private static final int shift; private final long[] array; - private long rawIndex(int i) { + static { + int scale = unsafe.arrayIndexScale(long[].class); + if ((scale & (scale - 1)) != 0) + throw new Error("data type scale not a power of two"); + shift = 31 - Integer.numberOfLeadingZeros(scale); + } + + private long checkedByteOffset(int i) { if (i < 0 || i >= array.length) throw new IndexOutOfBoundsException("index " + i); - return base + (long) i * scale; + + return byteOffset(i); + } + + private static long byteOffset(int i) { + return ((long) i << shift) + base; } /** - * 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); } /** @@ -79,17 +88,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(); } /** @@ -108,7 +108,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); } /** @@ -118,7 +122,7 @@ 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); } /** @@ -129,7 +133,7 @@ public class AtomicLongArray implements java.io.Serializable { * @since 1.6 */ public final void lazySet(int i, long newValue) { - unsafe.putOrderedLong(array, rawIndex(i), newValue); + unsafe.putOrderedLong(array, checkedByteOffset(i), newValue); } @@ -142,16 +146,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 @@ -160,13 +165,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. * *

May fail spuriously * and does not provide ordering guarantees, so is only rarely an @@ -188,12 +196,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); } /** @@ -203,12 +206,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); } /** @@ -219,10 +217,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; } } @@ -234,12 +232,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); } /** @@ -249,12 +242,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); } /** @@ -265,22 +253,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(',').append(' '); + } } } diff --git a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java index ca1ca6507a0..e898758f522 100644 --- a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java +++ b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java @@ -38,8 +38,8 @@ package java.util.concurrent.atomic; /** * An {@code AtomicMarkableReference} maintains an object reference * along with a mark bit, that can be updated atomically. - *

- *

Implementation note. This implementation maintains markable + * + *

Implementation note: This implementation maintains markable * references by creating internal objects representing "boxed" * [reference, boolean] pairs. * @@ -47,17 +47,21 @@ package java.util.concurrent.atomic; * @author Doug Lea * @param The type of object referred to by this reference */ -public class AtomicMarkableReference { +public class AtomicMarkableReference { - private static class ReferenceBooleanPair { - private final T reference; - private final boolean bit; - ReferenceBooleanPair(T r, boolean i) { - reference = r; bit = i; + private static class Pair { + final T reference; + final boolean mark; + private Pair(T reference, boolean mark) { + this.reference = reference; + this.mark = mark; + } + static Pair of(T reference, boolean mark) { + return new Pair(reference, mark); } } - private final AtomicReference> atomicRef; + private volatile Pair pair; /** * Creates a new {@code AtomicMarkableReference} with the given @@ -67,7 +71,7 @@ public class AtomicMarkableReference { * @param initialMark the initial mark */ public AtomicMarkableReference(V initialRef, boolean initialMark) { - atomicRef = new AtomicReference> (new ReferenceBooleanPair(initialRef, initialMark)); + pair = Pair.of(initialRef, initialMark); } /** @@ -76,7 +80,7 @@ public class AtomicMarkableReference { * @return the current value of the reference */ public V getReference() { - return atomicRef.get().reference; + return pair.reference; } /** @@ -85,7 +89,7 @@ public class AtomicMarkableReference { * @return the current value of the mark */ public boolean isMarked() { - return atomicRef.get().bit; + return pair.mark; } /** @@ -97,9 +101,9 @@ public class AtomicMarkableReference { * @return the current value of the reference */ public V get(boolean[] markHolder) { - ReferenceBooleanPair p = atomicRef.get(); - markHolder[0] = p.bit; - return p.reference; + Pair pair = this.pair; + markHolder[0] = pair.mark; + return pair.reference; } /** @@ -122,13 +126,8 @@ public class AtomicMarkableReference { V newReference, boolean expectedMark, boolean newMark) { - ReferenceBooleanPair current = atomicRef.get(); - return expectedReference == current.reference && - expectedMark == current.bit && - ((newReference == current.reference && newMark == current.bit) || - atomicRef.weakCompareAndSet(current, - new ReferenceBooleanPair(newReference, - newMark))); + return compareAndSet(expectedReference, newReference, + expectedMark, newMark); } /** @@ -147,13 +146,13 @@ public class AtomicMarkableReference { V newReference, boolean expectedMark, boolean newMark) { - ReferenceBooleanPair current = atomicRef.get(); - return expectedReference == current.reference && - expectedMark == current.bit && - ((newReference == current.reference && newMark == current.bit) || - atomicRef.compareAndSet(current, - new ReferenceBooleanPair(newReference, - newMark))); + Pair current = pair; + return + expectedReference == current.reference && + expectedMark == current.mark && + ((newReference == current.reference && + newMark == current.mark) || + casPair(current, Pair.of(newReference, newMark))); } /** @@ -163,9 +162,9 @@ public class AtomicMarkableReference { * @param newMark the new value for the mark */ public void set(V newReference, boolean newMark) { - ReferenceBooleanPair current = atomicRef.get(); - if (newReference != current.reference || newMark != current.bit) - atomicRef.set(new ReferenceBooleanPair(newReference, newMark)); + Pair current = pair; + if (newReference != current.reference || newMark != current.mark) + this.pair = Pair.of(newReference, newMark); } /** @@ -182,11 +181,32 @@ public class AtomicMarkableReference { * @return true if successful */ public boolean attemptMark(V expectedReference, boolean newMark) { - ReferenceBooleanPair current = atomicRef.get(); - return expectedReference == current.reference && - (newMark == current.bit || - atomicRef.compareAndSet - (current, new ReferenceBooleanPair(expectedReference, - newMark))); + Pair current = pair; + return + expectedReference == current.reference && + (newMark == current.mark || + casPair(current, Pair.of(expectedReference, newMark))); + } + + // Unsafe mechanics + + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); + private static final long pairOffset = + objectFieldOffset(UNSAFE, "pair", AtomicMarkableReference.class); + + private boolean casPair(Pair cmp, Pair val) { + return UNSAFE.compareAndSwapObject(this, pairOffset, 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/jdk/src/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java index 6aca62c5cc7..b5fd65ab48f 100644 --- a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java +++ b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java @@ -51,24 +51,35 @@ public class AtomicReferenceArray implements java.io.Serializable { private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final int base = unsafe.arrayBaseOffset(Object[].class); - private static final int scale = unsafe.arrayIndexScale(Object[].class); + private static final int shift; private final Object[] array; - private long rawIndex(int i) { + static { + int scale = unsafe.arrayIndexScale(Object[].class); + if ((scale & (scale - 1)) != 0) + throw new Error("data type scale not a power of two"); + shift = 31 - Integer.numberOfLeadingZeros(scale); + } + + private long checkedByteOffset(int i) { if (i < 0 || i >= array.length) throw new IndexOutOfBoundsException("index " + i); - return base + (long) i * scale; + + return byteOffset(i); + } + + private static long byteOffset(int i) { + return ((long) i << shift) + base; } /** - * Creates a new AtomicReferenceArray of given length. + * Creates a new AtomicReferenceArray of the given length, with all + * elements initially null. + * * @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); } /** @@ -79,18 +90,8 @@ public class AtomicReferenceArray 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(); } /** @@ -109,7 +110,11 @@ public class AtomicReferenceArray 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); } /** @@ -119,7 +124,7 @@ public class AtomicReferenceArray 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); } /** @@ -130,7 +135,7 @@ public class AtomicReferenceArray implements java.io.Serializable { * @since 1.6 */ public final void lazySet(int i, E newValue) { - unsafe.putOrderedObject(array, rawIndex(i), newValue); + unsafe.putOrderedObject(array, checkedByteOffset(i), newValue); } @@ -143,9 +148,10 @@ public class AtomicReferenceArray 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; } } @@ -153,6 +159,7 @@ public class AtomicReferenceArray 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 @@ -160,8 +167,11 @@ public class AtomicReferenceArray 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); } /** @@ -183,12 +193,21 @@ public class AtomicReferenceArray 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(',').append(' '); + } } } diff --git a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java index 854c02c12a3..412eca36eb3 100644 --- a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java +++ b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java @@ -39,7 +39,7 @@ package java.util.concurrent.atomic; * An {@code AtomicStampedReference} maintains an object reference * along with an integer "stamp", that can be updated atomically. * - *

Implementation note. This implementation maintains stamped + *

Implementation note: This implementation maintains stamped * references by creating internal objects representing "boxed" * [reference, integer] pairs. * @@ -47,17 +47,21 @@ package java.util.concurrent.atomic; * @author Doug Lea * @param The type of object referred to by this reference */ -public class AtomicStampedReference { +public class AtomicStampedReference { - private static class ReferenceIntegerPair { - private final T reference; - private final int integer; - ReferenceIntegerPair(T r, int i) { - reference = r; integer = i; + private static class Pair { + final T reference; + final int stamp; + private Pair(T reference, int stamp) { + this.reference = reference; + this.stamp = stamp; + } + static Pair of(T reference, int stamp) { + return new Pair(reference, stamp); } } - private final AtomicReference> atomicRef; + private volatile Pair pair; /** * Creates a new {@code AtomicStampedReference} with the given @@ -67,8 +71,7 @@ public class AtomicStampedReference { * @param initialStamp the initial stamp */ public AtomicStampedReference(V initialRef, int initialStamp) { - atomicRef = new AtomicReference> - (new ReferenceIntegerPair(initialRef, initialStamp)); + pair = Pair.of(initialRef, initialStamp); } /** @@ -77,7 +80,7 @@ public class AtomicStampedReference { * @return the current value of the reference */ public V getReference() { - return atomicRef.get().reference; + return pair.reference; } /** @@ -86,7 +89,7 @@ public class AtomicStampedReference { * @return the current value of the stamp */ public int getStamp() { - return atomicRef.get().integer; + return pair.stamp; } /** @@ -98,9 +101,9 @@ public class AtomicStampedReference { * @return the current value of the reference */ public V get(int[] stampHolder) { - ReferenceIntegerPair p = atomicRef.get(); - stampHolder[0] = p.integer; - return p.reference; + Pair pair = this.pair; + stampHolder[0] = pair.stamp; + return pair.reference; } /** @@ -119,18 +122,12 @@ public class AtomicStampedReference { * @param newStamp the new value for the stamp * @return true if successful */ - public boolean weakCompareAndSet(V expectedReference, - V newReference, - int expectedStamp, - int newStamp) { - ReferenceIntegerPair current = atomicRef.get(); - return expectedReference == current.reference && - expectedStamp == current.integer && - ((newReference == current.reference && - newStamp == current.integer) || - atomicRef.weakCompareAndSet(current, - new ReferenceIntegerPair(newReference, - newStamp))); + public boolean weakCompareAndSet(V expectedReference, + V newReference, + int expectedStamp, + int newStamp) { + return compareAndSet(expectedReference, newReference, + expectedStamp, newStamp); } /** @@ -145,18 +142,17 @@ public class AtomicStampedReference { * @param newStamp the new value for the stamp * @return true if successful */ - public boolean compareAndSet(V expectedReference, - V newReference, - int expectedStamp, - int newStamp) { - ReferenceIntegerPair current = atomicRef.get(); - return expectedReference == current.reference && - expectedStamp == current.integer && + public boolean compareAndSet(V expectedReference, + V newReference, + int expectedStamp, + int newStamp) { + Pair current = pair; + return + expectedReference == current.reference && + expectedStamp == current.stamp && ((newReference == current.reference && - newStamp == current.integer) || - atomicRef.compareAndSet(current, - new ReferenceIntegerPair(newReference, - newStamp))); + newStamp == current.stamp) || + casPair(current, Pair.of(newReference, newStamp))); } @@ -167,9 +163,9 @@ public class AtomicStampedReference { * @param newStamp the new value for the stamp */ public void set(V newReference, int newStamp) { - ReferenceIntegerPair current = atomicRef.get(); - if (newReference != current.reference || newStamp != current.integer) - atomicRef.set(new ReferenceIntegerPair(newReference, newStamp)); + Pair current = pair; + if (newReference != current.reference || newStamp != current.stamp) + this.pair = Pair.of(newReference, newStamp); } /** @@ -186,11 +182,32 @@ public class AtomicStampedReference { * @return true if successful */ public boolean attemptStamp(V expectedReference, int newStamp) { - ReferenceIntegerPair current = atomicRef.get(); - return expectedReference == current.reference && - (newStamp == current.integer || - atomicRef.compareAndSet(current, - new ReferenceIntegerPair(expectedReference, - newStamp))); + Pair current = pair; + return + expectedReference == current.reference && + (newStamp == current.stamp || + casPair(current, Pair.of(expectedReference, newStamp))); + } + + // Unsafe mechanics + + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); + private static final long pairOffset = + objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class); + + private boolean casPair(Pair cmp, Pair val) { + return UNSAFE.compareAndSwapObject(this, pairOffset, 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/jdk/src/share/classes/java/util/concurrent/locks/Condition.java b/jdk/src/share/classes/java/util/concurrent/locks/Condition.java index c1774bc25dd..f3fb607a47e 100644 --- a/jdk/src/share/classes/java/util/concurrent/locks/Condition.java +++ b/jdk/src/share/classes/java/util/concurrent/locks/Condition.java @@ -308,18 +308,21 @@ public interface Condition { * condition still does not hold. Typical uses of this method take * the following form: * - *

-     * synchronized boolean aMethod(long timeout, TimeUnit unit) {
-     *   long nanosTimeout = unit.toNanos(timeout);
-     *   while (!conditionBeingWaitedFor) {
-     *     if (nanosTimeout > 0)
-     *         nanosTimeout = theCondition.awaitNanos(nanosTimeout);
-     *      else
-     *        return false;
+     *  
 {@code
+     * boolean aMethod(long timeout, TimeUnit unit) {
+     *   long nanos = unit.toNanos(timeout);
+     *   lock.lock();
+     *   try {
+     *     while (!conditionBeingWaitedFor()) {
+     *       if (nanos <= 0L)
+     *         return false;
+     *       nanos = theCondition.awaitNanos(nanos);
+     *     }
+     *     // ...
+     *   } finally {
+     *     lock.unlock();
      *   }
-     *   // ...
-     * }
-     * 
+ * }}
* *

Design note: This method requires a nanosecond argument so * as to avoid truncation errors in reporting remaining times. @@ -408,18 +411,21 @@ public interface Condition { * *

The return value indicates whether the deadline has elapsed, * which can be used as follows: - *

-     * synchronized boolean aMethod(Date deadline) {
+     *  
 {@code
+     * boolean aMethod(Date deadline) {
      *   boolean stillWaiting = true;
-     *   while (!conditionBeingWaitedFor) {
-     *     if (stillWaiting)
-     *         stillWaiting = theCondition.awaitUntil(deadline);
-     *      else
-     *        return false;
+     *   lock.lock();
+     *   try {
+     *     while (!conditionBeingWaitedFor()) {
+     *       if (!stillWaiting)
+     *         return false;
+     *       stillWaiting = theCondition.awaitUntil(deadline);
+     *     }
+     *     // ...
+     *   } finally {
+     *     lock.unlock();
      *   }
-     *   // ...
-     * }
-     * 
+ * }}
* *

Implementation Considerations * @@ -450,6 +456,15 @@ public interface Condition { *

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}. + * + *

Implementation Considerations + * + *

An implementation may (and typically does) require that the + * current thread hold the lock associated with this {@code + * Condition} when this method is called. Implementations must + * document this precondition and any actions taken if the lock is + * not held. Typically, an exception such as {@link + * IllegalMonitorStateException} will be thrown. */ void signal(); @@ -459,6 +474,15 @@ public interface Condition { *

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}. + * + *

Implementation Considerations + * + *

An implementation may (and typically does) require that the + * current thread hold the lock associated with this {@code + * Condition} when this method is called. Implementations must + * document this precondition and any actions taken if the lock is + * not held. Typically, an exception such as {@link + * IllegalMonitorStateException} will be thrown. */ void signalAll(); } diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index 365de2bc17b..ced686efd87 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -734,8 +734,8 @@ java/util/concurrent/locks/Lock/TimedAcquireLeak.java generic-all # Fails on solaris-sparc -server (Set not equal to copy. 1) java/util/EnumSet/EnumSetBash.java solaris-sparc -# Need to be marked othervm, or changed to be samevm safe -java/util/WeakHashMap/GCDuringIteration.java generic-all +# Fails on solaris-sparc, see 7011857 +java/util/concurrent/Phaser/FickleRegister.java solaris-sparc ############################################################################ diff --git a/jdk/test/java/util/WeakHashMap/GCDuringIteration.java b/jdk/test/java/util/WeakHashMap/GCDuringIteration.java index 938322b7db2..482d04426ca 100644 --- a/jdk/test/java/util/WeakHashMap/GCDuringIteration.java +++ b/jdk/test/java/util/WeakHashMap/GCDuringIteration.java @@ -33,18 +33,17 @@ import java.util.*; import java.util.concurrent.CountDownLatch; public class GCDuringIteration { - static void finalizeTillYouDrop() { - System.gc(); // Enqueue all finalizables + private static void waitForFinalizersToRun() { + for (int i = 0; i < 2; i++) + tryWaitForFinalizersToRun(); + } - System.runFinalization(); // Drain finalizer queue - - // There may be a straggler finalizable object still being - // finalized by the dedicated finalizer thread. Enqueue one - // more finalizable object, and wait for it to be finalized. - final CountDownLatch latch = new CountDownLatch(1); - new Object() { protected void finalize() { latch.countDown(); }}; + private static void tryWaitForFinalizersToRun() { System.gc(); - try { latch.await(); } + final CountDownLatch fin = new CountDownLatch(1); + new Object() { protected void finalize() { fin.countDown(); }}; + System.gc(); + try { fin.await(); } catch (InterruptedException ie) { throw new Error(ie); } } @@ -101,7 +100,9 @@ public class GCDuringIteration { { int first = firstValue(map); final Iterator> it = map.entrySet().iterator(); - foos[first] = null; finalizeTillYouDrop(); + foos[first] = null; + for (int i = 0; i < 10 && map.size() != first; i++) + tryWaitForFinalizersToRun(); equal(map.size(), first); checkIterator(it, first-1); equal(map.size(), first); @@ -113,11 +114,14 @@ public class GCDuringIteration { final Iterator> it = map.entrySet().iterator(); it.next(); // protects first entry System.out.println(map.values()); - foos[first] = null; finalizeTillYouDrop(); + foos[first] = null; + tryWaitForFinalizersToRun() equal(map.size(), first+1); System.out.println(map.values()); checkIterator(it, first-1); - finalizeTillYouDrop(); // first entry no longer protected + // first entry no longer protected + for (int i = 0; i < 10 && map.size() != first; i++) + tryWaitForFinalizersToRun(); equal(map.size(), first); equal(firstValue(map), first-1); } @@ -127,12 +131,15 @@ public class GCDuringIteration { final Iterator> it = map.entrySet().iterator(); it.next(); // protects first entry System.out.println(map.values()); - foos[first] = foos[first-1] = null; finalizeTillYouDrop(); + foos[first] = foos[first-1] = null; + tryWaitForFinalizersToRun(); equal(map.size(), first); equal(firstValue(map), first); System.out.println(map.values()); checkIterator(it, first-2); - finalizeTillYouDrop(); // first entry no longer protected + // first entry no longer protected + for (int i = 0; i < 10 && map.size() != first-1; i++) + tryWaitForFinalizersToRun(); equal(map.size(), first-1); equal(firstValue(map), first-2); } @@ -143,12 +150,15 @@ public class GCDuringIteration { it.next(); // protects first entry it.hasNext(); // protects second entry System.out.println(map.values()); - foos[first] = foos[first-1] = null; finalizeTillYouDrop(); + foos[first] = foos[first-1] = null; + tryWaitForFinalizersToRun(); equal(firstValue(map), first); equal(map.size(), first+1); System.out.println(map.values()); checkIterator(it, first-1); - finalizeTillYouDrop(); // first entry no longer protected + // first entry no longer protected + for (int i = 0; i < 10 && map.size() != first-1; i++) + tryWaitForFinalizersToRun(); equal(map.size(), first-1); equal(firstValue(map), first-2); } @@ -158,13 +168,16 @@ public class GCDuringIteration { final Iterator> it = map.entrySet().iterator(); it.next(); // protects first entry System.out.println(map.values()); - foos[first] = foos[first-1] = null; finalizeTillYouDrop(); + foos[first] = foos[first-1] = null; + tryWaitForFinalizersToRun(); it.remove(); equal(firstValue(map), first-2); equal(map.size(), first-1); System.out.println(map.values()); checkIterator(it, first-2); - finalizeTillYouDrop(); + // first entry no longer protected + for (int i = 0; i < 10 && map.size() != first-1; i++) + tryWaitForFinalizersToRun(); equal(map.size(), first-1); equal(firstValue(map), first-2); } @@ -176,12 +189,14 @@ public class GCDuringIteration { it.remove(); it.hasNext(); // protects second entry System.out.println(map.values()); - foos[first] = foos[first-1] = null; finalizeTillYouDrop(); + foos[first] = foos[first-1] = null; + tryWaitForFinalizersToRun(); equal(firstValue(map), first-1); equal(map.size(), first); System.out.println(map.values()); checkIterator(it, first-1); - finalizeTillYouDrop(); + for (int i = 0; i < 10 && map.size() != first-1; i++) + tryWaitForFinalizersToRun(); equal(map.size(), first-1); equal(firstValue(map), first-2); } @@ -191,12 +206,13 @@ public class GCDuringIteration { final Iterator> it = map.entrySet().iterator(); it.hasNext(); // protects first entry Arrays.fill(foos, null); - finalizeTillYouDrop(); + tryWaitForFinalizersToRun(); equal(map.size(), 1); System.out.println(map.values()); equal(it.next().getValue(), first); check(! it.hasNext()); - finalizeTillYouDrop(); + for (int i = 0; i < 10 && map.size() != 0; i++) + tryWaitForFinalizersToRun(); equal(map.size(), 0); check(map.isEmpty()); } diff --git a/jdk/test/java/util/concurrent/BlockingQueue/CancelledProducerConsumerLoops.java b/jdk/test/java/util/concurrent/BlockingQueue/CancelledProducerConsumerLoops.java index c12a57ec708..34f0722d8bd 100644 --- a/jdk/test/java/util/concurrent/BlockingQueue/CancelledProducerConsumerLoops.java +++ b/jdk/test/java/util/concurrent/BlockingQueue/CancelledProducerConsumerLoops.java @@ -34,7 +34,7 @@ /* * @test * @bug 4486658 - * @compile CancelledProducerConsumerLoops.java + * @compile -source 1.5 CancelledProducerConsumerLoops.java * @run main/timeout=7000 CancelledProducerConsumerLoops * @summary Checks for responsiveness of blocking queues to cancellation. * Runs under the assumption that ITERS computations require more than @@ -119,48 +119,24 @@ public class CancelledProducerConsumerLoops { } } - static final class LTQasSQ extends LinkedTransferQueue { - LTQasSQ() { super(); } - public void put(T x) { - try { super.transfer(x); } - catch (InterruptedException ex) { throw new Error(); } - } - private final static long serialVersionUID = 42; - } - - static final class HalfSyncLTQ extends LinkedTransferQueue { - HalfSyncLTQ() { super(); } - public void put(T x) { - if (ThreadLocalRandom.current().nextBoolean()) - super.put(x); - else { - try { super.transfer(x); } - catch (InterruptedException ex) { throw new Error(); } - } - } - private final static long serialVersionUID = 42; - } - static void oneTest(int pairs, int iters) throws Exception { oneRun(new ArrayBlockingQueue(CAPACITY), pairs, iters); oneRun(new LinkedBlockingQueue(CAPACITY), pairs, iters); oneRun(new LinkedBlockingDeque(CAPACITY), pairs, iters); + oneRun(new LinkedTransferQueue(), pairs, iters); oneRun(new SynchronousQueue(), pairs, iters / 8); - /* TODO: unbounded queue implementations are prone to OOME + /* PriorityBlockingQueue is unbounded oneRun(new PriorityBlockingQueue(iters / 2 * pairs), pairs, iters / 4); - oneRun(new LinkedTransferQueue(), pairs, iters); - oneRun(new LTQasSQ(), pairs, iters); - oneRun(new HalfSyncLTQ(), pairs, iters); */ } - static abstract class Stage implements Callable { + abstract static class Stage implements Callable { final BlockingQueue queue; final CyclicBarrier barrier; final int iters; - Stage (BlockingQueue q, CyclicBarrier b, int iters) { + Stage(BlockingQueue q, CyclicBarrier b, int iters) { queue = q; barrier = b; this.iters = iters; diff --git a/jdk/test/java/util/concurrent/BlockingQueue/MultipleProducersSingleConsumerLoops.java b/jdk/test/java/util/concurrent/BlockingQueue/MultipleProducersSingleConsumerLoops.java index 1f85c511e59..d57939fc374 100644 --- a/jdk/test/java/util/concurrent/BlockingQueue/MultipleProducersSingleConsumerLoops.java +++ b/jdk/test/java/util/concurrent/BlockingQueue/MultipleProducersSingleConsumerLoops.java @@ -34,7 +34,7 @@ /* * @test * @bug 4486658 - * @compile MultipleProducersSingleConsumerLoops.java + * @compile -source 1.5 MultipleProducersSingleConsumerLoops.java * @run main/timeout=3600 MultipleProducersSingleConsumerLoops * @summary multiple producers and single consumer using blocking queues */ @@ -87,35 +87,11 @@ public class MultipleProducersSingleConsumerLoops { throw new Error(); } - static final class LTQasSQ extends LinkedTransferQueue { - LTQasSQ() { super(); } - public void put(T x) { - try { super.transfer(x); } - catch (InterruptedException ex) { throw new Error(); } - } - private final static long serialVersionUID = 42; - } - - static final class HalfSyncLTQ extends LinkedTransferQueue { - HalfSyncLTQ() { super(); } - public void put(T x) { - if (ThreadLocalRandom.current().nextBoolean()) - super.put(x); - else { - try { super.transfer(x); } - catch (InterruptedException ex) { throw new Error(); } - } - } - private final static long serialVersionUID = 42; - } - static void oneTest(int producers, int iters) throws Exception { oneRun(new ArrayBlockingQueue(CAPACITY), producers, iters); oneRun(new LinkedBlockingQueue(CAPACITY), producers, iters); oneRun(new LinkedBlockingDeque(CAPACITY), producers, iters); oneRun(new LinkedTransferQueue(), producers, iters); - oneRun(new LTQasSQ(), producers, iters); - oneRun(new HalfSyncLTQ(), producers, iters); // Don't run PBQ since can legitimately run out of memory // if (print) @@ -129,11 +105,11 @@ public class MultipleProducersSingleConsumerLoops { oneRun(new ArrayBlockingQueue(CAPACITY, true), producers, iters); } - static abstract class Stage implements Runnable { + abstract static class Stage implements Runnable { final int iters; final BlockingQueue queue; final CyclicBarrier barrier; - Stage (BlockingQueue q, CyclicBarrier b, int iters) { + Stage(BlockingQueue q, CyclicBarrier b, int iters) { queue = q; barrier = b; this.iters = iters; diff --git a/jdk/test/java/util/concurrent/BlockingQueue/ProducerConsumerLoops.java b/jdk/test/java/util/concurrent/BlockingQueue/ProducerConsumerLoops.java index f2b4a5b334d..9f18ab53c89 100644 --- a/jdk/test/java/util/concurrent/BlockingQueue/ProducerConsumerLoops.java +++ b/jdk/test/java/util/concurrent/BlockingQueue/ProducerConsumerLoops.java @@ -34,7 +34,7 @@ /* * @test * @bug 4486658 - * @compile ProducerConsumerLoops.java + * @compile -source 1.5 ProducerConsumerLoops.java * @run main/timeout=3600 ProducerConsumerLoops * @summary multiple producers and consumers using blocking queues */ @@ -87,35 +87,11 @@ public class ProducerConsumerLoops { throw new Error(); } - static final class LTQasSQ extends LinkedTransferQueue { - LTQasSQ() { super(); } - public void put(T x) { - try { super.transfer(x); } - catch (InterruptedException ex) { throw new Error(); } - } - private final static long serialVersionUID = 42; - } - - static final class HalfSyncLTQ extends LinkedTransferQueue { - HalfSyncLTQ() { super(); } - public void put(T x) { - if (ThreadLocalRandom.current().nextBoolean()) - super.put(x); - else { - try { super.transfer(x); } - catch (InterruptedException ex) { throw new Error(); } - } - } - private final static long serialVersionUID = 42; - } - static void oneTest(int pairs, int iters) throws Exception { oneRun(new ArrayBlockingQueue(CAPACITY), pairs, iters); oneRun(new LinkedBlockingQueue(CAPACITY), pairs, iters); oneRun(new LinkedBlockingDeque(CAPACITY), pairs, iters); oneRun(new LinkedTransferQueue(), pairs, iters); - oneRun(new LTQasSQ(), pairs, iters); - oneRun(new HalfSyncLTQ(), pairs, iters); oneRun(new PriorityBlockingQueue(), pairs, iters); oneRun(new SynchronousQueue(), pairs, iters); @@ -126,11 +102,11 @@ public class ProducerConsumerLoops { oneRun(new ArrayBlockingQueue(CAPACITY, true), pairs, iters); } - static abstract class Stage implements Runnable { + abstract static class Stage implements Runnable { final int iters; final BlockingQueue queue; final CyclicBarrier barrier; - Stage (BlockingQueue q, CyclicBarrier b, int iters) { + Stage(BlockingQueue q, CyclicBarrier b, int iters) { queue = q; barrier = b; this.iters = iters; diff --git a/jdk/test/java/util/concurrent/BlockingQueue/SingleProducerMultipleConsumerLoops.java b/jdk/test/java/util/concurrent/BlockingQueue/SingleProducerMultipleConsumerLoops.java index 1550bb22277..85699bc7d56 100644 --- a/jdk/test/java/util/concurrent/BlockingQueue/SingleProducerMultipleConsumerLoops.java +++ b/jdk/test/java/util/concurrent/BlockingQueue/SingleProducerMultipleConsumerLoops.java @@ -34,7 +34,7 @@ /* * @test * @bug 4486658 - * @compile SingleProducerMultipleConsumerLoops.java + * @compile -source 1.5 SingleProducerMultipleConsumerLoops.java * @run main/timeout=600 SingleProducerMultipleConsumerLoops * @summary check ordering for blocking queues with 1 producer and multiple consumers */ @@ -73,35 +73,11 @@ public class SingleProducerMultipleConsumerLoops { throw new Error(); } - static final class LTQasSQ extends LinkedTransferQueue { - LTQasSQ() { super(); } - public void put(T x) { - try { super.transfer(x); } - catch (InterruptedException ex) { throw new Error(); } - } - private final static long serialVersionUID = 42; - } - - static final class HalfSyncLTQ extends LinkedTransferQueue { - HalfSyncLTQ() { super(); } - public void put(T x) { - if (ThreadLocalRandom.current().nextBoolean()) - super.put(x); - else { - try { super.transfer(x); } - catch (InterruptedException ex) { throw new Error(); } - } - } - private final static long serialVersionUID = 42; - } - static void oneTest(int consumers, int iters) throws Exception { oneRun(new ArrayBlockingQueue(CAPACITY), consumers, iters); oneRun(new LinkedBlockingQueue(CAPACITY), consumers, iters); oneRun(new LinkedBlockingDeque(CAPACITY), consumers, iters); oneRun(new LinkedTransferQueue(), consumers, iters); - oneRun(new LTQasSQ(), consumers, iters); - oneRun(new HalfSyncLTQ(), consumers, iters); oneRun(new PriorityBlockingQueue(), consumers, iters); oneRun(new SynchronousQueue(), consumers, iters); if (print) @@ -110,12 +86,12 @@ public class SingleProducerMultipleConsumerLoops { oneRun(new ArrayBlockingQueue(CAPACITY, true), consumers, iters); } - static abstract class Stage implements Runnable { + abstract static class Stage implements Runnable { final int iters; final BlockingQueue queue; final CyclicBarrier barrier; volatile int result; - Stage (BlockingQueue q, CyclicBarrier b, int iters) { + Stage(BlockingQueue q, CyclicBarrier b, int iters) { queue = q; barrier = b; this.iters = iters; diff --git a/jdk/test/java/util/concurrent/ConcurrentQueues/IteratorWeakConsistency.java b/jdk/test/java/util/concurrent/ConcurrentQueues/IteratorWeakConsistency.java index 78ce8d71194..dabb2b9de2a 100644 --- a/jdk/test/java/util/concurrent/ConcurrentQueues/IteratorWeakConsistency.java +++ b/jdk/test/java/util/concurrent/ConcurrentQueues/IteratorWeakConsistency.java @@ -53,7 +53,9 @@ public class IteratorWeakConsistency { test(new LinkedTransferQueue()); // Other concurrent queues (e.g. ArrayBlockingQueue) do not // currently have weakly consistent iterators. - // test(new ArrayBlockingQueue(20)); + // As of 2010-09, ArrayBlockingQueue passes this test, but + // does not fully implement weak consistency. + test(new ArrayBlockingQueue(20)); } void test(Queue q) { diff --git a/jdk/test/java/util/concurrent/Executors/AutoShutdown.java b/jdk/test/java/util/concurrent/Executors/AutoShutdown.java index e6f7c1a3d5a..fb4976ff878 100644 --- a/jdk/test/java/util/concurrent/Executors/AutoShutdown.java +++ b/jdk/test/java/util/concurrent/Executors/AutoShutdown.java @@ -24,6 +24,7 @@ /* * @test * @bug 6399443 + * @run main/othervm AutoShutdown * @summary Check for auto-shutdown and gc of singleThreadExecutors * @author Martin Buchholz */ diff --git a/jdk/test/java/util/concurrent/Phaser/Basic.java b/jdk/test/java/util/concurrent/Phaser/Basic.java index e394cee94f6..a358873c2ec 100644 --- a/jdk/test/java/util/concurrent/Phaser/Basic.java +++ b/jdk/test/java/util/concurrent/Phaser/Basic.java @@ -52,15 +52,16 @@ public class Basic { check(phaser.isTerminated()); int unarriverParties = phaser.getUnarrivedParties(); int registeredParties = phaser.getRegisteredParties(); - equal(phaser.arrive(), -1); - equal(phaser.arriveAndDeregister(), -1); - equal(phaser.arriveAndAwaitAdvance(), -1); - equal(phaser.bulkRegister(10), -1); - equal(phaser.getPhase(), -1); - equal(phaser.register(), -1); + int phase = phaser.getPhase(); + check(phase < 0); + equal(phase, phaser.arrive()); + equal(phase, phaser.arriveAndDeregister()); + equal(phase, phaser.arriveAndAwaitAdvance()); + equal(phase, phaser.bulkRegister(10)); + equal(phase, phaser.register()); try { - equal(phaser.awaitAdvanceInterruptibly(0), -1); - equal(phaser.awaitAdvanceInterruptibly(0, 10, SECONDS), -1); + equal(phase, phaser.awaitAdvanceInterruptibly(0)); + equal(phase, phaser.awaitAdvanceInterruptibly(0, 10, SECONDS)); } catch (Exception ie) { unexpected(ie); } @@ -94,10 +95,9 @@ public class Basic { } int phase = atTheStartingGate.getPhase(); equal(phase, atTheStartingGate.arrive()); - int AwaitPhase = atTheStartingGate.awaitAdvanceInterruptibly(phase, - 10, - SECONDS); - if (expectNextPhase) check(AwaitPhase == (phase + 1)); + int awaitPhase = atTheStartingGate.awaitAdvanceInterruptibly + (phase, 10, SECONDS); + if (expectNextPhase) check(awaitPhase == (phase + 1)); pass(); } catch (Throwable t) { @@ -271,18 +271,19 @@ public class Basic { // Phaser is terminated while threads are waiting //---------------------------------------------------------------- try { - Phaser phaser = new Phaser(3); - Iterator awaiters = awaiterIterator(phaser); for (int i = 0; i < 4; i++) { + Phaser phaser = new Phaser(3); + Iterator awaiters = awaiterIterator(phaser); Arriver a1 = awaiters.next(); a1.start(); Arriver a2 = awaiters.next(); a2.start(); toTheStartingGate(); while (phaser.getArrivedParties() < 2) Thread.yield(); + equal(0, phaser.getPhase()); phaser.forceTermination(); a1.join(); a2.join(); - check(a1.phase == -1); - check(a2.phase == -1); + equal(0 + Integer.MIN_VALUE, a1.phase); + equal(0 + Integer.MIN_VALUE, a2.phase); int arrivedParties = phaser.getArrivedParties(); checkTerminated(phaser); equal(phaser.getArrivedParties(), arrivedParties); diff --git a/jdk/test/java/util/concurrent/Phaser/FickleRegister.java b/jdk/test/java/util/concurrent/Phaser/FickleRegister.java new file mode 100644 index 00000000000..9ce91c5de44 --- /dev/null +++ b/jdk/test/java/util/concurrent/Phaser/FickleRegister.java @@ -0,0 +1,150 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * 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 + */ + +/* + * @test + * @summary stress test for register/arriveAndDeregister + * @run main FickleRegister 300 + */ + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; + +public class FickleRegister { + final AtomicLong count = new AtomicLong(0); + final long testDurationMillisDefault = 10L * 1000L; + final long testDurationMillis; + final long quittingTimeNanos; + final int chunkSize = 1000; + + FickleRegister(String[] args) { + testDurationMillis = (args.length > 0) ? + Long.valueOf(args[0]) : testDurationMillisDefault; + quittingTimeNanos = System.nanoTime() + + testDurationMillis * 1000L * 1000L; + } + + class Runner extends CheckedRunnable { + final Phaser p; + Runner(Phaser phaser) { p = phaser; } + public void realRun() { + int prevPhase = -1; + for (int k = 1;; k++) { + for (int i = 0; i < chunkSize; i++) { + int phase = p.register(); + if (phase < 0) break; + check(phase > prevPhase); + prevPhase = phase; + equal(phase, p.arriveAndDeregister()); + check(phase < p.awaitAdvance(phase)); + } + if (System.nanoTime() - quittingTimeNanos > 0) { + count.getAndAdd(k * chunkSize); + break; + } + } + } + } + + void test(String[] args) throws Throwable { + final Phaser parent = new Phaser() { + protected boolean onAdvance(int phase, int parties) { + return false; + } + }; + + final Phaser child1 = new Phaser(parent); + final Phaser child2 = new Phaser(parent); + final Phaser subchild1 = new Phaser(child1); + final Phaser subchild2 = new Phaser(child2); + final Phaser[] phasers = { + parent, child1, child2, subchild1, subchild2 + }; + + int reps = 4; + ArrayList threads = new ArrayList(); + for (int j = 0; j < reps; ++j) { + threads.add(new Thread(new Runner(subchild1))); + threads.add(new Thread(new Runner(child1))); + threads.add(new Thread(new Runner(parent))); + threads.add(new Thread(new Runner(child2))); + threads.add(new Thread(new Runner(subchild2))); + } + + for (Thread thread : threads) + thread.start(); + + for (Thread thread : threads) + thread.join(); + + System.out.println("Parent: " + parent); + System.out.println("Child1: " + child1); + System.out.println("Child2: " + child2); + System.out.println("Subchild1: " + subchild1); + System.out.println("Subchild2: " + subchild2); + System.out.println("Iterations:" + count.get()); + + for (Phaser phaser : phasers) { + check(phaser.getPhase() > 0); + equal(0, phaser.getRegisteredParties()); + equal(0, phaser.getUnarrivedParties()); + equal(parent.getPhase(), phaser.getPhase()); + } + } + + //--------------------- Infrastructure --------------------------- + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) pass(); + else fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + new FickleRegister(args).instanceMain(args);} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} + + abstract class CheckedRunnable implements Runnable { + protected abstract void realRun() throws Throwable; + + public final void run() { + try {realRun();} catch (Throwable t) {unexpected(t);} + } + } +} diff --git a/jdk/test/java/util/concurrent/Phaser/PhaseOverflow.java b/jdk/test/java/util/concurrent/Phaser/PhaseOverflow.java new file mode 100644 index 00000000000..da8b5367072 --- /dev/null +++ b/jdk/test/java/util/concurrent/Phaser/PhaseOverflow.java @@ -0,0 +1,158 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Martin Buchholz and 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 + */ + +/* + * @test + * @summary Test Phaser phase integer overflow behavior + */ + +import java.util.concurrent.Phaser; +import java.lang.reflect.Field; + +public class PhaseOverflow { + Field stateField; + + void checkState(Phaser phaser, + int phase, int parties, int unarrived) { + equal(phase, phaser.getPhase()); + equal(parties, phaser.getRegisteredParties()); + equal(unarrived, phaser.getUnarrivedParties()); + } + + void test(String[] args) throws Throwable { + stateField = Phaser.class.getDeclaredField("state"); + stateField.setAccessible(true); + testLeaf(); + testTiered(); + } + + void testLeaf() throws Throwable { + Phaser phaser = new Phaser(); + // this is extremely dependent on internal representation + stateField.setLong(phaser, ((Integer.MAX_VALUE - 1L) << 32) | 1L); + checkState(phaser, Integer.MAX_VALUE - 1, 0, 0); + phaser.register(); + checkState(phaser, Integer.MAX_VALUE - 1, 1, 1); + phaser.arrive(); + checkState(phaser, Integer.MAX_VALUE, 1, 1); + phaser.arrive(); + checkState(phaser, 0, 1, 1); + phaser.arrive(); + checkState(phaser, 1, 1, 1); + } + + int phaseInc(int phase) { return (phase + 1) & Integer.MAX_VALUE; } + + void testTiered() throws Throwable { + Phaser root = new Phaser(); + // this is extremely dependent on internal representation + stateField.setLong(root, ((Integer.MAX_VALUE - 1L) << 32) | 1L); + checkState(root, Integer.MAX_VALUE - 1, 0, 0); + Phaser p1 = new Phaser(root, 1); + checkState(root, Integer.MAX_VALUE - 1, 1, 1); + checkState(p1, Integer.MAX_VALUE - 1, 1, 1); + Phaser p2 = new Phaser(root, 2); + checkState(root, Integer.MAX_VALUE - 1, 2, 2); + checkState(p2, Integer.MAX_VALUE - 1, 2, 2); + int ph = Integer.MAX_VALUE - 1; + for (int k = 0; k < 5; k++) { + checkState(root, ph, 2, 2); + checkState(p1, ph, 1, 1); + checkState(p2, ph, 2, 2); + p1.arrive(); + checkState(root, ph, 2, 1); + checkState(p1, ph, 1, 0); + checkState(p2, ph, 2, 2); + p2.arrive(); + checkState(root, ph, 2, 1); + checkState(p1, ph, 1, 0); + checkState(p2, ph, 2, 1); + p2.arrive(); + ph = phaseInc(ph); + checkState(root, ph, 2, 2); + checkState(p1, ph, 1, 1); + checkState(p2, ph, 2, 2); + } + equal(3, ph); + } + + void xtestTiered() throws Throwable { + Phaser root = new Phaser(); + stateField.setLong(root, ((Integer.MAX_VALUE - 1L) << 32) | 1L); + checkState(root, Integer.MAX_VALUE - 1, 0, 0); + Phaser p1 = new Phaser(root, 1); + checkState(root, Integer.MAX_VALUE - 1, 1, 1); + checkState(p1, Integer.MAX_VALUE - 1, 1, 1); + Phaser p2 = new Phaser(root, 2); + checkState(root, Integer.MAX_VALUE - 1, 2, 2); + checkState(p2, Integer.MAX_VALUE - 1, 2, 2); + int ph = Integer.MAX_VALUE - 1; + for (int k = 0; k < 5; k++) { + checkState(root, ph, 2, 2); + checkState(p1, ph, 1, 1); + checkState(p2, ph, 2, 2); + p1.arrive(); + checkState(root, ph, 2, 1); + checkState(p1, ph, 1, 0); + checkState(p2, ph, 2, 2); + p2.arrive(); + checkState(root, ph, 2, 1); + checkState(p1, ph, 1, 0); + checkState(p2, ph, 2, 1); + p2.arrive(); + ph = phaseInc(ph); + checkState(root, ph, 2, 2); + checkState(p1, ph, 1, 1); + checkState(p2, ph, 2, 2); + } + equal(3, ph); + } + + //--------------------- Infrastructure --------------------------- + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) pass(); + else fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + new PhaseOverflow().instanceMain(args);} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} diff --git a/jdk/test/java/util/concurrent/Phaser/TieredArriveLoops.java b/jdk/test/java/util/concurrent/Phaser/TieredArriveLoops.java new file mode 100644 index 00000000000..33a03f08dab --- /dev/null +++ b/jdk/test/java/util/concurrent/Phaser/TieredArriveLoops.java @@ -0,0 +1,117 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * 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 + */ + +/* + * @test + * @summary stress test for arrivals in a tiered phaser + * @run main TieredArriveLoops 300 + */ +import java.util.*; +import java.util.concurrent.*; + +public class TieredArriveLoops { + final long testDurationMillisDefault = 10L * 1000L; + final long testDurationMillis; + final long quittingTimeNanos; + + TieredArriveLoops(String[] args) { + testDurationMillis = (args.length > 0) ? + Long.valueOf(args[0]) : testDurationMillisDefault; + quittingTimeNanos = System.nanoTime() + + testDurationMillis * 1000L * 1000L; + } + + Runnable runner(final Phaser p) { + return new CheckedRunnable() { public void realRun() { + int prevPhase = p.register(); + while (!p.isTerminated()) { + int phase = p.awaitAdvance(p.arrive()); + if (phase < 0) + return; + equal(phase, (prevPhase + 1) & Integer.MAX_VALUE); + int ph = p.getPhase(); + check(ph < 0 || ph == phase); + prevPhase = phase; + } + }}; + } + + void test(String[] args) throws Throwable { + final Phaser parent = new Phaser(); + final Phaser child1 = new Phaser(parent); + final Phaser child2 = new Phaser(parent); + + Thread t1 = new Thread(runner(child1)); + Thread t2 = new Thread(runner(child2)); + t1.start(); + t2.start(); + + for (int prevPhase = 0, phase; ; prevPhase = phase) { + phase = child2.getPhase(); + check(phase >= prevPhase); + if (System.nanoTime() - quittingTimeNanos > 0) { + System.err.printf("phase=%d%n", phase); + child1.forceTermination(); + break; + } + } + + t1.join(); + t2.join(); + } + + //--------------------- Infrastructure --------------------------- + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) pass(); + else fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + new TieredArriveLoops(args).instanceMain(args);} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} + + abstract class CheckedRunnable implements Runnable { + protected abstract void realRun() throws Throwable; + + public final void run() { + try {realRun();} catch (Throwable t) {unexpected(t);} + } + } +} diff --git a/jdk/test/java/util/concurrent/ThreadPoolExecutor/CoreThreadTimeOut.java b/jdk/test/java/util/concurrent/ThreadPoolExecutor/CoreThreadTimeOut.java index 6bd37fe7035..547cd2130f0 100644 --- a/jdk/test/java/util/concurrent/ThreadPoolExecutor/CoreThreadTimeOut.java +++ b/jdk/test/java/util/concurrent/ThreadPoolExecutor/CoreThreadTimeOut.java @@ -31,47 +31,79 @@ import java.util.concurrent.*; public class CoreThreadTimeOut { - static volatile int passed = 0, failed = 0; - static void pass() { passed++; } - static void fail() { failed++; Thread.dumpStack(); } - static void unexpected(Throwable t) { failed++; t.printStackTrace(); } - static void check(boolean cond) { if (cond) pass(); else fail(); } - static void equal(Object x, Object y) { - if (x == null ? y == null : x.equals(y)) pass(); - else {System.out.println(x + " not equal to " + y); fail(); }} - static int countExecutorThreads() { + static class IdentifiableThreadFactory implements ThreadFactory { + static ThreadFactory defaultThreadFactory + = Executors.defaultThreadFactory(); + + public Thread newThread(Runnable r) { + Thread t = defaultThreadFactory.newThread(r); + t.setName("CoreThreadTimeOut-" + t.getName()); + return t; + } + } + + int countExecutorThreads() { Thread[] threads = new Thread[Thread.activeCount()+100]; Thread.enumerate(threads); int count = 0; for (Thread t : threads) - if (t != null && t.getName().matches("pool-[0-9]+-thread-[0-9]+")) + if (t != null && + t.getName().matches + ("CoreThreadTimeOut-pool-[0-9]+-thread-[0-9]+")) count++; return count; } - public static void main(String[] args) throws Throwable { + long millisElapsedSince(long t0) { + return (System.nanoTime() - t0) / (1000L * 1000L); + } + + void test(String[] args) throws Throwable { final int threadCount = 10; + final int timeoutMillis = 30; BlockingQueue q = new ArrayBlockingQueue(2*threadCount); ThreadPoolExecutor tpe = new ThreadPoolExecutor(threadCount, threadCount, - 30, TimeUnit.MILLISECONDS, - q); + timeoutMillis, TimeUnit.MILLISECONDS, + q, new IdentifiableThreadFactory()); equal(tpe.getCorePoolSize(), threadCount); check(! tpe.allowsCoreThreadTimeOut()); tpe.allowCoreThreadTimeOut(true); check(tpe.allowsCoreThreadTimeOut()); equal(countExecutorThreads(), 0); + long t0 = System.nanoTime(); for (int i = 0; i < threadCount; i++) tpe.submit(new Runnable() { public void run() {}}); - equal(countExecutorThreads(), threadCount); - Thread.sleep(500); + int count = countExecutorThreads(); + if (millisElapsedSince(t0) < timeoutMillis) + equal(count, threadCount); + while (countExecutorThreads() > 0 && + millisElapsedSince(t0) < 10 * 1000); equal(countExecutorThreads(), 0); tpe.shutdown(); check(tpe.allowsCoreThreadTimeOut()); + check(tpe.awaitTermination(10, TimeUnit.SECONDS)); System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); if (failed > 0) throw new Exception("Some tests failed"); } + + //--------------------- Infrastructure --------------------------- + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) pass(); + else fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + new CoreThreadTimeOut().instanceMain(args);} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} } From 8867f6b29823704312740f5c3bc8b46c9f151814 Mon Sep 17 00:00:00 2001 From: Keith McGuigan Date: Wed, 12 Jan 2011 11:47:35 -0500 Subject: [PATCH 4/6] 6436034: Instance filter doesn't filter event if it occurs in native method Use 'GetLocalInstance' JVMTI extension if it exists Reviewed-by: coleenp, dcubed --- jdk/src/share/back/debugInit.c | 65 +++++++--- jdk/src/share/back/eventFilter.c | 44 ++++++- jdk/src/share/instrument/JPLISAgent.c | 4 +- jdk/src/share/javavm/export/jvmti.h | 18 ++- .../com/sun/jdi/NativeInstanceFilter.java | 122 ++++++++++++++++++ .../com/sun/jdi/NativeInstanceFilterTarg.java | 56 ++++++++ 6 files changed, 284 insertions(+), 25 deletions(-) create mode 100644 jdk/test/com/sun/jdi/NativeInstanceFilter.java create mode 100644 jdk/test/com/sun/jdi/NativeInstanceFilterTarg.java diff --git a/jdk/src/share/back/debugInit.c b/jdk/src/share/back/debugInit.c index e09971b0a06..39c6ad53fba 100644 --- a/jdk/src/share/back/debugInit.c +++ b/jdk/src/share/back/debugInit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -133,27 +133,60 @@ set_event_notification(jvmtiEventMode mode, EventIndex ei) return error; } +typedef struct { + int major; + int minor; +} version_type; + +typedef struct { + version_type runtime; + version_type compiletime; +} compatible_versions_type; + +/* + * List of explicitly compatible JVMTI versions, specified as + * { runtime version, compile-time version } pairs. -1 is a wildcard. + */ +static int nof_compatible_versions = 3; +static compatible_versions_type compatible_versions_list[] = { + /* + * FIXUP: Allow version 0 to be compatible with anything + * Special check for FCS of 1.0. + */ + { { 0, -1 }, { -1, -1 } }, + { { -1, -1 }, { 0, -1 } }, + /* + * 1.2 is runtime compatible with 1.1 -- just make sure to check the + * version before using any new 1.2 features + */ + { { 1, 1 }, { 1, 2 } } +}; + + /* Logic to determine JVMTI version compatibility */ static jboolean compatible_versions(jint major_runtime, jint minor_runtime, jint major_compiletime, jint minor_compiletime) { -#if 1 /* FIXUP: We allow version 0 to be compatible with anything */ - /* Special check for FCS of 1.0. */ - if ( major_runtime == 0 || major_compiletime == 0 ) { - return JNI_TRUE; + /* + * First check to see if versions are explicitly compatible via the + * list specified above. + */ + int i; + for (i = 0; i < nof_compatible_versions; ++i) { + version_type runtime = compatible_versions_list[i].runtime; + version_type comptime = compatible_versions_list[i].compiletime; + + if ((major_runtime == runtime.major || runtime.major == -1) && + (minor_runtime == runtime.minor || runtime.minor == -1) && + (major_compiletime == comptime.major || comptime.major == -1) && + (minor_compiletime == comptime.minor || comptime.minor == -1)) { + return JNI_TRUE; + } } -#endif - /* Runtime major version must match. */ - if ( major_runtime != major_compiletime ) { - return JNI_FALSE; - } - /* Runtime minor version must be >= the version compiled with. */ - if ( minor_runtime < minor_compiletime ) { - return JNI_FALSE; - } - /* Assumed compatible */ - return JNI_TRUE; + + return major_runtime == major_compiletime && + minor_runtime >= minor_compiletime; } /* OnLoad startup: diff --git a/jdk/src/share/back/eventFilter.c b/jdk/src/share/back/eventFilter.c index b0c2bd19b98..3d816d37019 100644 --- a/jdk/src/share/back/eventFilter.c +++ b/jdk/src/share/back/eventFilter.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,6 +39,7 @@ #include "stepControl.h" #include "threadControl.h" #include "SDE.h" +#include "jvmti.h" typedef struct ClassFilter { jclass clazz; @@ -275,6 +276,24 @@ patternStringMatch(char *classname, const char *pattern) } } +static jboolean isVersionGte12x() { + jint version; + jvmtiError err = + JVMTI_FUNC_PTR(gdata->jvmti,GetVersionNumber)(gdata->jvmti, &version); + + if (err == JVMTI_ERROR_NONE) { + jint major, minor; + + major = (version & JVMTI_VERSION_MASK_MAJOR) + >> JVMTI_VERSION_SHIFT_MAJOR; + minor = (version & JVMTI_VERSION_MASK_MINOR) + >> JVMTI_VERSION_SHIFT_MINOR; + return (major > 1 || major == 1 && minor >= 2); + } else { + return JNI_FALSE; + } +} + /* Return the object instance in which the event occurred */ /* Return NULL if static or if an error occurs */ static jobject @@ -286,6 +305,14 @@ eventInstance(EventInfo *evinfo) jint modifiers = 0; jvmtiError error; + static jboolean got_version = JNI_FALSE; + static jboolean is_version_gte_12x = JNI_FALSE; + + if (!got_version) { + is_version_gte_12x = isVersionGte12x(); + got_version = JNI_TRUE; + } + switch (evinfo->ei) { case EI_SINGLE_STEP: case EI_BREAKPOINT: @@ -314,11 +341,18 @@ eventInstance(EventInfo *evinfo) /* fail if error or static (0x8) */ if (error == JVMTI_ERROR_NONE && thread!=NULL && (modifiers & 0x8) == 0) { FrameNumber fnum = 0; - /* get slot zero object "this" */ - error = JVMTI_FUNC_PTR(gdata->jvmti,GetLocalObject) - (gdata->jvmti, thread, fnum, 0, &object); - if (error != JVMTI_ERROR_NONE) + if (is_version_gte_12x) { + /* Use new 1.2.x function, GetLocalInstance */ + error = JVMTI_FUNC_PTR(gdata->jvmti,GetLocalInstance) + (gdata->jvmti, thread, fnum, &object); + } else { + /* get slot zero object "this" */ + error = JVMTI_FUNC_PTR(gdata->jvmti,GetLocalObject) + (gdata->jvmti, thread, fnum, 0, &object); + } + if (error != JVMTI_ERROR_NONE) { object = NULL; + } } return object; diff --git a/jdk/src/share/instrument/JPLISAgent.c b/jdk/src/share/instrument/JPLISAgent.c index adc9cf13355..7f6b2a0b67c 100644 --- a/jdk/src/share/instrument/JPLISAgent.c +++ b/jdk/src/share/instrument/JPLISAgent.c @@ -209,7 +209,7 @@ createNewJPLISAgent(JavaVM * vm, JPLISAgent **agent_ptr) { *agent_ptr = NULL; jnierror = (*vm)->GetEnv( vm, (void **) &jvmtienv, - JVMTI_VERSION); + JVMTI_VERSION_1_1); if ( jnierror != JNI_OK ) { initerror = JPLIS_INIT_ERROR_CANNOT_CREATE_NATIVE_AGENT; } else { @@ -990,7 +990,7 @@ retransformableEnvironment(JPLISAgent * agent) { } jnierror = (*agent->mJVM)->GetEnv( agent->mJVM, (void **) &retransformerEnv, - JVMTI_VERSION); + JVMTI_VERSION_1_1); if ( jnierror != JNI_OK ) { return NULL; } diff --git a/jdk/src/share/javavm/export/jvmti.h b/jdk/src/share/javavm/export/jvmti.h index c235f16376b..7e9f2216b55 100644 --- a/jdk/src/share/javavm/export/jvmti.h +++ b/jdk/src/share/javavm/export/jvmti.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,8 +41,9 @@ enum { JVMTI_VERSION_1 = 0x30010000, JVMTI_VERSION_1_0 = 0x30010000, JVMTI_VERSION_1_1 = 0x30010100, + JVMTI_VERSION_1_2 = 0x30010200, - JVMTI_VERSION = 0x30000000 + (1 * 0x10000) + (1 * 0x100) + 102 /* version: 1.1.102 */ + JVMTI_VERSION = 0x30000000 + (1 * 0x10000) + (2 * 0x100) + 1 /* version: 1.2.1 */ }; JNIEXPORT jint JNICALL @@ -1774,6 +1775,12 @@ typedef struct jvmtiInterface_1_ { jobject object, jlong* size_ptr); + /* 155 : Get Local Instance */ + jvmtiError (JNICALL *GetLocalInstance) (jvmtiEnv* env, + jthread thread, + jint depth, + jobject* value_ptr); + } jvmtiInterface_1; struct _jvmtiEnv { @@ -2031,6 +2038,12 @@ struct _jvmtiEnv { return functions->GetLocalObject(this, thread, depth, slot, value_ptr); } + jvmtiError GetLocalInstance(jthread thread, + jint depth, + jobject* value_ptr) { + return functions->GetLocalInstance(this, thread, depth, value_ptr); + } + jvmtiError GetLocalInt(jthread thread, jint depth, jint slot, @@ -2518,3 +2531,4 @@ struct _jvmtiEnv { #endif /* __cplusplus */ #endif /* !_JAVA_JVMTI_H_ */ + diff --git a/jdk/test/com/sun/jdi/NativeInstanceFilter.java b/jdk/test/com/sun/jdi/NativeInstanceFilter.java new file mode 100644 index 00000000000..90e714e6889 --- /dev/null +++ b/jdk/test/com/sun/jdi/NativeInstanceFilter.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 6426034 + * @summary Instance filter doesn't filter event if it occurs in native method + * + * @author Keith McGuigan + * + * @library scaffold + * @run build JDIScaffold VMConnection + * @compile -XDignore.symbol.file NativeInstanceFilterTarg.java + * @run main/othervm NativeInstanceFilter + */ + +/* + * This test tests instance filters for events generated from a native method + */ + +import java.util.*; +import com.sun.jdi.*; +import com.sun.jdi.event.*; +import com.sun.jdi.request.*; + +public class NativeInstanceFilter extends JDIScaffold { + + static int unfilteredEvents = 0; + + public static void main(String args[]) throws Exception { + new NativeInstanceFilter().startTests(); + } + + public NativeInstanceFilter() { + super(); + } + + static EventRequestManager requestManager = null; + static MethodExitRequest request = null; + + private void listen() { + TargetAdapter adapter = new TargetAdapter() { + EventSet set = null; + ObjectReference instance = null; + + public boolean eventSetReceived(EventSet set) { + this.set = set; + return false; + } + + public boolean methodExited(MethodExitEvent event) { + String name = event.method().name(); + if (instance == null && name.equals("latch")) { + // Grab the instance (return value) and set up as filter + System.out.println("Setting up instance filter"); + instance = (ObjectReference)event.returnValue(); + requestManager.deleteEventRequest(request); + request = requestManager.createMethodExitRequest(); + request.addInstanceFilter(instance); + request.enable(); + } else if (instance != null && name.equals("intern")) { + // If not for the filter, this will be called twice + System.out.println("method exit event (String.intern())"); + ++unfilteredEvents; + } + set.resume(); + return false; + } + }; + addListener(adapter); + } + + + protected void runTests() throws Exception { + String[] args = new String[2]; + args[0] = "-connect"; + args[1] = "com.sun.jdi.CommandLineLaunch:main=NativeInstanceFilterTarg"; + + connect(args); + waitForVMStart(); + + // VM has started, but hasn't started running the test program yet. + requestManager = vm().eventRequestManager(); + ReferenceType referenceType = + resumeToPrepareOf("NativeInstanceFilterTarg").referenceType(); + + request = requestManager.createMethodExitRequest(); + request.enable(); + + listen(); + + vm().resume(); + + waitForVMDeath(); + + if (unfilteredEvents != 1) { + throw new Exception( + "Failed: Event from native frame not filtered out."); + } + System.out.println("Passed: Event filtered out."); + } +} diff --git a/jdk/test/com/sun/jdi/NativeInstanceFilterTarg.java b/jdk/test/com/sun/jdi/NativeInstanceFilterTarg.java new file mode 100644 index 00000000000..48a21b43aba --- /dev/null +++ b/jdk/test/com/sun/jdi/NativeInstanceFilterTarg.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import sun.misc.Version; + +public class NativeInstanceFilterTarg { + + public static void main(String args[]) { + boolean runTest = jvmSupportsJVMTI_12x(); + String s1 = "abc"; + String s2 = "def"; + latch(s1); + s1.intern(); + if (runTest) { + s2.intern(); // this is the call that generates events that ought + // to be filtered out. + } else { + System.out.println("Neutering test since JVMTI 1.2 not supported"); + } + } + + // Used by debugger to get an instance to filter with + public static String latch(String s) { return s; } + + public static boolean jvmSupportsJVMTI_12x() { + // This fix requires the JVM to support JVMTI 1.2, which doesn't + // happen until HSX 20.0, build 05. + int major = Version.jvmMajorVersion(); + int minor = Version.jvmMinorVersion(); + int micro = Version.jvmMicroVersion(); + int build = Version.jvmBuildNumber(); + + return (major > 20 || major == 20 && + (minor > 0 || micro > 0 || build >= 5)); + } +} From 07549260db3653825d1393e1104eae654716b883 Mon Sep 17 00:00:00 2001 From: Stuart Marks Date: Wed, 12 Jan 2011 13:52:09 -0800 Subject: [PATCH 5/6] 7008713: diamond conversion of kerberos5 and security tools Reviewed-by: weijun --- .../classes/sun/security/krb5/Config.java | 21 ++++++++-------- .../classes/sun/security/krb5/KdcComm.java | 6 ++--- .../sun/security/krb5/PrincipalName.java | 4 +-- .../classes/sun/security/krb5/Realm.java | 4 +-- .../security/krb5/internal/Authenticator.java | 2 +- .../krb5/internal/AuthorizationData.java | 3 +-- .../security/krb5/internal/EncAPRepPart.java | 2 +- .../security/krb5/internal/HostAddresses.java | 5 ++-- .../sun/security/krb5/internal/KDCReq.java | 2 +- .../security/krb5/internal/KDCReqBody.java | 6 ++--- .../sun/security/krb5/internal/KRBCred.java | 2 +- .../sun/security/krb5/internal/KRBError.java | 2 +- .../security/krb5/internal/KrbCredInfo.java | 2 +- .../sun/security/krb5/internal/LastReq.java | 2 +- .../internal/ccache/FileCredentialsCache.java | 2 +- .../security/krb5/internal/crypto/EType.java | 2 +- .../security/krb5/internal/ktab/KeyTab.java | 4 +-- .../classes/sun/security/tools/JarSigner.java | 25 ++++++++----------- .../classes/sun/security/tools/KeyTool.java | 19 +++++++------- .../security/tools/policytool/PolicyTool.java | 10 +++----- jdk/test/sun/security/krb5/IPv6.java | 4 +-- .../sun/security/krb5/auto/CleanState.java | 4 +-- jdk/test/sun/security/krb5/auto/Context.java | 6 ++--- .../krb5/auto/HttpNegotiateServer.java | 4 +-- jdk/test/sun/security/krb5/auto/KDC.java | 10 ++++---- .../krb5/auto/LoginModuleOptions.java | 4 +-- .../sun/security/krb5/tools/KtabCheck.java | 2 +- 27 files changed, 75 insertions(+), 84 deletions(-) diff --git a/jdk/src/share/classes/sun/security/krb5/Config.java b/jdk/src/share/classes/sun/security/krb5/Config.java index 272c3b1e6ca..09017bf7a3e 100644 --- a/jdk/src/share/classes/sun/security/krb5/Config.java +++ b/jdk/src/share/classes/sun/security/krb5/Config.java @@ -528,7 +528,7 @@ public class Config { } }))); String Line; - Vector v = new Vector (); + Vector v = new Vector<>(); String previous = null; while ((Line = br.readLine()) != null) { // ignore comments and blank line in the configuration file. @@ -589,7 +589,7 @@ public class Config { throw new KrbException("I/O error while reading" + " configuration file."); } - Hashtable table = new Hashtable (); + Hashtable table = new Hashtable<>(); for (int i = 0; i < v.size(); i++) { String line = v.elementAt(i).trim(); if (line.equalsIgnoreCase("[realms]")) { @@ -598,7 +598,7 @@ public class Config { if ((count == v.size()) || (v.elementAt(count).startsWith("["))) { Hashtable>> temp = - new Hashtable>>(); + new Hashtable<>(); temp = parseRealmField(v, i + 1, count); table.put("realms", temp); i = count - 1; @@ -611,7 +611,7 @@ public class Config { if ((count == v.size()) || (v.elementAt(count).startsWith("["))) { Hashtable>> temp = - new Hashtable>>(); + new Hashtable<>(); temp = parseRealmField(v, i + 1, count); table.put("capaths", temp); i = count - 1; @@ -729,7 +729,7 @@ public class Config { * Parses key-value pairs under a stanza name. */ private Hashtable parseField(Vector v, int start, int end) { - Hashtable table = new Hashtable (); + Hashtable table = new Hashtable<>(); String line; for (int i = start; i < end; i++) { line = v.elementAt(i); @@ -751,7 +751,7 @@ public class Config { * information for the realm given within a pair of braces. */ private Hashtable>> parseRealmField(Vector v, int start, int end) { - Hashtable>> table = new Hashtable>> (); + Hashtable>> table = new Hashtable<>(); String line; for (int i = start; i < end; i++) { line = v.elementAt(i).trim(); @@ -791,10 +791,9 @@ public class Config { * Parses key-value pairs within each braces under [realms]. */ private Hashtable> parseRealmFieldEx(Vector v, int start, int end) { - Hashtable> table = - new Hashtable> (); - Vector keyVector = new Vector (); - Vector nameVector = new Vector (); + Hashtable> table = new Hashtable<>(); + Vector keyVector = new Vector<>(); + Vector nameVector = new Vector<>(); String line = ""; String key; for (int i = start; i < end; i++) { @@ -899,7 +898,7 @@ public class Config { } st = new StringTokenizer(default_enctypes, delim); int len = st.countTokens(); - ArrayList ls = new ArrayList (len); + ArrayList ls = new ArrayList<>(len); int type; for (int i = 0; i < len; i++) { type = getType(st.nextToken()); diff --git a/jdk/src/share/classes/sun/security/krb5/KdcComm.java b/jdk/src/share/classes/sun/security/krb5/KdcComm.java index 22e54afe5e8..bb113392db1 100644 --- a/jdk/src/share/classes/sun/security/krb5/KdcComm.java +++ b/jdk/src/share/classes/sun/security/krb5/KdcComm.java @@ -462,7 +462,7 @@ public final class KdcComm { */ static class KdcAccessibility { // Known bad KDCs - private static Set bads = new HashSet(); + private static Set bads = new HashSet<>(); private static synchronized void addBad(String kdc) { if (DEBUG) { @@ -492,9 +492,9 @@ public final class KdcComm { // Returns a preferred KDC list by putting the bad ones at the end private static synchronized String[] list(String kdcList) { StringTokenizer st = new StringTokenizer(kdcList); - List list = new ArrayList(); + List list = new ArrayList<>(); if (badPolicy == BpType.TRY_LAST) { - List badkdcs = new ArrayList(); + List badkdcs = new ArrayList<>(); while (st.hasMoreTokens()) { String t = st.nextToken(); if (bads.contains(t)) badkdcs.add(t); diff --git a/jdk/src/share/classes/sun/security/krb5/PrincipalName.java b/jdk/src/share/classes/sun/security/krb5/PrincipalName.java index 647f7199b3f..11d11dbfa0e 100644 --- a/jdk/src/share/classes/sun/security/krb5/PrincipalName.java +++ b/jdk/src/share/classes/sun/security/krb5/PrincipalName.java @@ -244,7 +244,7 @@ public class PrincipalName if (subDer.getTag() != DerValue.tag_SequenceOf) { throw new Asn1Exception(Krb5.ASN1_BAD_ID); } - Vector v = new Vector (); + Vector v = new Vector<>(); DerValue subSubDer; while(subDer.getData().available() > 0) { subSubDer = subDer.getData().getDerValue(); @@ -299,7 +299,7 @@ public class PrincipalName // Code repetition, realm parsed again by class Realm protected static String[] parseName(String name) { - Vector tempStrings = new Vector (); + Vector tempStrings = new Vector<>(); String temp = name; int i = 0; int componentStart = 0; diff --git a/jdk/src/share/classes/sun/security/krb5/Realm.java b/jdk/src/share/classes/sun/security/krb5/Realm.java index 88f679de7a5..f7b450ec6be 100644 --- a/jdk/src/share/classes/sun/security/krb5/Realm.java +++ b/jdk/src/share/classes/sun/security/krb5/Realm.java @@ -359,12 +359,12 @@ public class Realm implements Cloneable { } String tempTarget = null, tempRealm = null; - Stack iStack = new Stack (); + Stack iStack = new Stack<>(); /* * I don't expect any more than a handful of intermediaries. */ - Vector tempList = new Vector (8, 8); + Vector tempList = new Vector<>(8, 8); /* * The initiator at first location. diff --git a/jdk/src/share/classes/sun/security/krb5/internal/Authenticator.java b/jdk/src/share/classes/sun/security/krb5/internal/Authenticator.java index 28a44cc514c..67f70387a99 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/Authenticator.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/Authenticator.java @@ -176,7 +176,7 @@ public class Authenticator { * @exception IOException if an I/O error occurs while reading encoded data. */ public byte[] asn1Encode() throws Asn1Exception, IOException { - Vector v = new Vector(); + Vector v = new Vector<>(); DerOutputStream temp = new DerOutputStream(); temp.putInteger(BigInteger.valueOf(authenticator_vno)); v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x00), temp.toByteArray())); diff --git a/jdk/src/share/classes/sun/security/krb5/internal/AuthorizationData.java b/jdk/src/share/classes/sun/security/krb5/internal/AuthorizationData.java index bbec348c146..9a9de81932f 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/AuthorizationData.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/AuthorizationData.java @@ -99,8 +99,7 @@ public class AuthorizationData implements Cloneable { * @exception IOException if an I/O error occurs while reading encoded data. */ public AuthorizationData(DerValue der) throws Asn1Exception, IOException { - Vector v = - new Vector(); + Vector v = new Vector<>(); if (der.getTag() != DerValue.tag_Sequence) { throw new Asn1Exception(Krb5.ASN1_BAD_ID); } diff --git a/jdk/src/share/classes/sun/security/krb5/internal/EncAPRepPart.java b/jdk/src/share/classes/sun/security/krb5/internal/EncAPRepPart.java index f8fb0aead5b..fdebf1de980 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/EncAPRepPart.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/EncAPRepPart.java @@ -133,7 +133,7 @@ public class EncAPRepPart { * @exception IOException if an I/O error occurs while reading encoded data. */ public byte[] asn1Encode() throws Asn1Exception, IOException { - Vector v = new Vector(); + Vector v = new Vector<>(); DerOutputStream temp = new DerOutputStream(); v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x00), ctime.asn1Encode())); diff --git a/jdk/src/share/classes/sun/security/krb5/internal/HostAddresses.java b/jdk/src/share/classes/sun/security/krb5/internal/HostAddresses.java index df51f3a3536..1cd1e4ac44f 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/HostAddresses.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/HostAddresses.java @@ -179,7 +179,7 @@ public class HostAddresses implements Cloneable { */ public HostAddresses(DerValue encoding) throws Asn1Exception, IOException { - Vector tempAddresses = new Vector (); + Vector tempAddresses = new Vector<>(); DerValue der = null; while (encoding.getData().available() > 0) { der = encoding.getData().getDerValue(); @@ -265,8 +265,7 @@ public class HostAddresses implements Cloneable { if (addresses == null || addresses.length == 0) return null; - ArrayList ipAddrs = - new ArrayList (addresses.length); + ArrayList ipAddrs = new ArrayList<>(addresses.length); for (int i = 0; i < addresses.length; i++) { try { diff --git a/jdk/src/share/classes/sun/security/krb5/internal/KDCReq.java b/jdk/src/share/classes/sun/security/krb5/internal/KDCReq.java index 0dfd3ccf904..16591e80404 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/KDCReq.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/KDCReq.java @@ -150,7 +150,7 @@ public class KDCReq { if (subsubDer.getTag() != DerValue.tag_SequenceOf) { throw new Asn1Exception(Krb5.ASN1_BAD_ID); } - Vector v = new Vector(); + Vector v = new Vector<>(); while (subsubDer.getData().available() > 0) { v.addElement(new PAData(subsubDer.getData().getDerValue())); } diff --git a/jdk/src/share/classes/sun/security/krb5/internal/KDCReqBody.java b/jdk/src/share/classes/sun/security/krb5/internal/KDCReqBody.java index 4af4dad0c13..a23d452885d 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/KDCReqBody.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/KDCReqBody.java @@ -158,7 +158,7 @@ public class KDCReqBody { throw new Asn1Exception(Krb5.ASN1_BAD_ID); } der = encoding.getData().getDerValue(); - Vector v = new Vector (); + Vector v = new Vector<>(); if ((der.getTag() & (byte)0x1F) == (byte)0x08) { subDer = der.getData().getDerValue(); @@ -183,7 +183,7 @@ public class KDCReqBody { encAuthorizationData = EncryptedData.parse(encoding.getData(), (byte)0x0A, true); } if (encoding.getData().available() > 0) { - Vector tempTickets = new Vector (); + Vector tempTickets = new Vector<>(); der = encoding.getData().getDerValue(); if ((der.getTag() & (byte)0x1F) == (byte)0x0B) { subDer = der.getData().getDerValue(); @@ -216,7 +216,7 @@ public class KDCReqBody { * */ public byte[] asn1Encode(int msgType) throws Asn1Exception, IOException { - Vector v = new Vector (); + Vector v = new Vector<>(); v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), kdcOptions.asn1Encode())); if (msgType == Krb5.KRB_AS_REQ) { if (cname != null) { diff --git a/jdk/src/share/classes/sun/security/krb5/internal/KRBCred.java b/jdk/src/share/classes/sun/security/krb5/internal/KRBCred.java index 5fbe7ad8e03..ff719f432c2 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/KRBCred.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/KRBCred.java @@ -134,7 +134,7 @@ public class KRBCred { if (subsubDer.getTag() != DerValue.tag_SequenceOf) { throw new Asn1Exception(Krb5.ASN1_BAD_ID); } - Vector v = new Vector(); + Vector v = new Vector<>(); while (subsubDer.getData().available() > 0) { v.addElement(new Ticket(subsubDer.getData().getDerValue())); } diff --git a/jdk/src/share/classes/sun/security/krb5/internal/KRBError.java b/jdk/src/share/classes/sun/security/krb5/internal/KRBError.java index ac8667f0686..ed17cd8212a 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/KRBError.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/KRBError.java @@ -260,7 +260,7 @@ public class KRBError implements java.io.Serializable { private void parsePAData(byte[] data) throws IOException, Asn1Exception { DerValue derPA = new DerValue(data); - List paList = new ArrayList(); + List paList = new ArrayList<>(); while (derPA.data.available() > 0) { // read the PA-DATA DerValue tmp = derPA.data.getDerValue(); diff --git a/jdk/src/share/classes/sun/security/krb5/internal/KrbCredInfo.java b/jdk/src/share/classes/sun/security/krb5/internal/KrbCredInfo.java index 7c2fbff849d..63ebe03d3c6 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/KrbCredInfo.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/KrbCredInfo.java @@ -157,7 +157,7 @@ public class KrbCredInfo { * @exception IOException if an I/O error occurs while reading encoded data. */ public byte[] asn1Encode() throws Asn1Exception, IOException { - Vector v = new Vector (); + Vector v = new Vector<>(); v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), key.asn1Encode())); if (prealm != null) v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), prealm.asn1Encode())); diff --git a/jdk/src/share/classes/sun/security/krb5/internal/LastReq.java b/jdk/src/share/classes/sun/security/krb5/internal/LastReq.java index 578d955f45b..45ff8a81274 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/LastReq.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/LastReq.java @@ -77,7 +77,7 @@ public class LastReq { */ public LastReq(DerValue encoding) throws Asn1Exception, IOException { - Vector v= new Vector (); + Vector v= new Vector<>(); if (encoding.getTag() != DerValue.tag_Sequence) { throw new Asn1Exception(Krb5.ASN1_BAD_ID); } diff --git a/jdk/src/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java b/jdk/src/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java index 4f874903f79..46e5985f76f 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java @@ -490,7 +490,7 @@ public class FileCredentialsCache extends CredentialsCache private static String exec(String c) { StringTokenizer st = new StringTokenizer(c); - Vector v = new Vector (); + Vector v = new Vector<>(); while (st.hasMoreTokens()) { v.addElement(st.nextToken()); } diff --git a/jdk/src/share/classes/sun/security/krb5/internal/crypto/EType.java b/jdk/src/share/classes/sun/security/krb5/internal/crypto/EType.java index 90c0be67dae..85ba264944d 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/crypto/EType.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/crypto/EType.java @@ -257,7 +257,7 @@ public abstract class EType { + configName); } - List list = new ArrayList (answer.length); + List list = new ArrayList<>(answer.length); for (int i = 0; i < answer.length; i++) { if (EncryptionKey.findKey(answer[i], keys) != null) { list.add(answer[i]); diff --git a/jdk/src/share/classes/sun/security/krb5/internal/ktab/KeyTab.java b/jdk/src/share/classes/sun/security/krb5/internal/ktab/KeyTab.java index e0624e4db03..3c680fde166 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/ktab/KeyTab.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/ktab/KeyTab.java @@ -57,7 +57,7 @@ public class KeyTab implements KeyTabConstants { private static KeyTab singleton = null; private static final boolean DEBUG = Krb5.DEBUG; private static String name; - private Vector entries = new Vector (); + private Vector entries = new Vector<>(); private KeyTab(String filename) throws IOException, RealmException { init(filename); @@ -240,7 +240,7 @@ public class KeyTab implements KeyTabConstants { KeyTabEntry entry; EncryptionKey key; int size = entries.size(); - ArrayList keys = new ArrayList (size); + ArrayList keys = new ArrayList<>(size); for (int i = size-1; i >= 0; i--) { entry = entries.elementAt(i); diff --git a/jdk/src/share/classes/sun/security/tools/JarSigner.java b/jdk/src/share/classes/sun/security/tools/JarSigner.java index 03809d37812..42d3bf57fd8 100644 --- a/jdk/src/share/classes/sun/security/tools/JarSigner.java +++ b/jdk/src/share/classes/sun/security/tools/JarSigner.java @@ -123,19 +123,19 @@ public class JarSigner { // or the default keystore, never null String keystore; // key store file - List crlfiles = new ArrayList(); // CRL files to add + List crlfiles = new ArrayList<>(); // CRL files to add boolean nullStream = false; // null keystore input stream (NONE) boolean token = false; // token-based keystore String jarfile; // jar files to sign or verify String alias; // alias to sign jar with - List ckaliases = new ArrayList(); // aliases in -verify + List ckaliases = new ArrayList<>(); // aliases in -verify char[] storepass; // keystore password boolean protectedPath; // protected authentication path String storetype; // keystore type String providerName; // provider name Vector providers = null; // list of providers // arguments for provider constructors - HashMap providerArgs = new HashMap(); + HashMap providerArgs = new HashMap<>(); char[] keypass; // private key password String sigfile; // name of .SF file String sigalg; // name of signature algorithm @@ -236,7 +236,7 @@ public class JarSigner { if (crlfiles.size() > 0 || autoCRL) { CertificateFactory fac = CertificateFactory.getInstance("X509"); - List list = new ArrayList(); + List list = new ArrayList<>(); for (String file: crlfiles) { Collection tmp = KeyTool.loadCRLs(file); for (CRL crl: tmp) { @@ -606,7 +606,7 @@ public class JarSigner { try { jf = new JarFile(jarName, true); - Vector entriesVec = new Vector(); + Vector entriesVec = new Vector<>(); byte[] buffer = new byte[8192]; Enumeration entries = jf.entries(); @@ -633,8 +633,7 @@ public class JarSigner { // The map to record display info, only used when -verbose provided // key: signer info string // value: the list of files with common key - Map> output = - new LinkedHashMap>(); + Map> output = new LinkedHashMap<>(); if (man != null) { if (verbose != null) System.out.println(); @@ -1000,8 +999,7 @@ public class JarSigner { .append(signTimeForm.format(source)).append("]").toString(); } - private Map cacheForInKS = - new IdentityHashMap(); + private Map cacheForInKS = new IdentityHashMap<>(); private int inKeyStoreForOneSigner(CodeSigner signer) { if (cacheForInKS.containsKey(signer)) { @@ -1044,8 +1042,7 @@ public class JarSigner { return result; } - Hashtable storeHash = - new Hashtable(); + Hashtable storeHash = new Hashtable<>(); int inKeyStore(CodeSigner[] signers) { @@ -1175,7 +1172,7 @@ public class JarSigner { * generated one. (This may invalidate existing signatures!) */ BASE64Encoder encoder = new JarBASE64Encoder(); - Vector mfFiles = new Vector(); + Vector mfFiles = new Vector<>(); boolean wasSigned = false; @@ -1531,7 +1528,7 @@ public class JarSigner { return false; } - Map cacheForSignerInfo = new IdentityHashMap(); + Map cacheForSignerInfo = new IdentityHashMap<>(); /** * Returns a string of singer info, with a newline at the end @@ -1655,7 +1652,7 @@ public class JarSigner { } } } - Set tas = new HashSet(); + Set tas = new HashSet<>(); try { KeyStore caks = KeyTool.getCacertsKeyStore(); if (caks != null) { diff --git a/jdk/src/share/classes/sun/security/tools/KeyTool.java b/jdk/src/share/classes/sun/security/tools/KeyTool.java index 5ce08316f35..897d407a1c2 100644 --- a/jdk/src/share/classes/sun/security/tools/KeyTool.java +++ b/jdk/src/share/classes/sun/security/tools/KeyTool.java @@ -153,11 +153,11 @@ public final class KeyTool { private KeyStore caks = null; // "cacerts" keystore private char[] srcstorePass = null; private String srcstoretype = null; - private Set passwords = new HashSet (); + private Set passwords = new HashSet<>(); private String startDate = null; - private List ids = new ArrayList (); // used in GENCRL - private List v3ext = new ArrayList (); + private List ids = new ArrayList<>(); // used in GENCRL + private List v3ext = new ArrayList<>(); enum Command { CERTREQ("Generates.a.certificate.request", @@ -2091,7 +2091,7 @@ public final class KeyTool { */ public static List readCRLsFromCert(X509Certificate cert) throws Exception { - List crls = new ArrayList(); + List crls = new ArrayList<>(); CRLDistributionPointsExtension ext = X509CertImpl.toImpl(cert).getCRLDistributionPointsExtension(); if (ext == null) return crls; @@ -2258,7 +2258,7 @@ public final class KeyTool { if (jarfile != null) { JarFile jf = new JarFile(jarfile, true); Enumeration entries = jf.entries(); - Set ss = new HashSet(); + Set ss = new HashSet<>(); byte[] buffer = new byte[8192]; int pos = 0; while (entries.hasMoreElements()) { @@ -3347,7 +3347,7 @@ public final class KeyTool { } // start building chain - Vector chain = new Vector(2); + Vector chain = new Vector<>(2); if (buildChain((X509Certificate)certToVerify, chain, certs)) { Certificate[] newChain = new Certificate[chain.size()]; // buildChain() returns chain with self-signed root-cert first and @@ -3873,8 +3873,7 @@ public final class KeyTool { break; case 2: // EKU if(value != null) { - Vector v = - new Vector (); + Vector v = new Vector<>(); for (String s: value.split(",")) { int p = oneOf(s, "anyExtendedKeyUsage", @@ -3944,7 +3943,7 @@ public final class KeyTool { } if(value != null) { List accessDescriptions = - new ArrayList(); + new ArrayList<>(); String[] ps = value.split(","); for(String item: ps) { colonpos = item.indexOf(':'); @@ -4228,7 +4227,7 @@ class Pair { } public static Pair of(A a, B b) { - return new Pair(a,b); + return new Pair<>(a,b); } } diff --git a/jdk/src/share/classes/sun/security/tools/policytool/PolicyTool.java b/jdk/src/share/classes/sun/security/tools/policytool/PolicyTool.java index f310504a204..cc40a08e594 100644 --- a/jdk/src/share/classes/sun/security/tools/policytool/PolicyTool.java +++ b/jdk/src/share/classes/sun/security/tools/policytool/PolicyTool.java @@ -643,7 +643,7 @@ public class PolicyTool { Class pc = Class.forName(type, true, Thread.currentThread().getContextClassLoader()); Constructor c = null; - Vector objects = new Vector(2); + Vector objects = new Vector<>(2); if (name != null) objects.add(name); if (actions != null) objects.add(actions); switch (objects.size()) { @@ -1722,8 +1722,7 @@ class ToolDialog extends Dialog { new PolicyParser.GrantEntry(signedby, codebase); // get the new Principals - LinkedList prins = - new LinkedList(); + LinkedList prins = new LinkedList<>(); TaggedList prinList = (TaggedList)getComponent(PE_PRIN_LIST); for (int i = 0; i < prinList.getItemCount(); i++) { prins.add((PolicyParser.PrincipalEntry)prinList.getObject(i)); @@ -1731,8 +1730,7 @@ class ToolDialog extends Dialog { ge.principals = prins; // get the new Permissions - Vector perms = - new Vector(); + Vector perms = new Vector<>(); TaggedList permList = (TaggedList)getComponent(PE_PERM_LIST); for (int i = 0; i < permList.getItemCount(); i++) { perms.addElement((PolicyParser.PermissionEntry)permList.getObject(i)); @@ -3649,7 +3647,7 @@ class NoDisplayException extends RuntimeException { * This is a java.awt.List that bind an Object to each String it holds. */ class TaggedList extends List { - private java.util.List data = new LinkedList(); + private java.util.List data = new LinkedList<>(); public TaggedList(int i, boolean b) { super(i, b); } diff --git a/jdk/test/sun/security/krb5/IPv6.java b/jdk/test/sun/security/krb5/IPv6.java index 58abbe9739e..4b4f2a88767 100644 --- a/jdk/test/sun/security/krb5/IPv6.java +++ b/jdk/test/sun/security/krb5/IPv6.java @@ -78,8 +78,8 @@ public class IPv6 { try { Subject subject = new Subject(); Krb5LoginModule krb5 = new Krb5LoginModule(); - Map map = new HashMap(); - Map shared = new HashMap(); + Map map = new HashMap<>(); + Map shared = new HashMap<>(); map.put("debug", "true"); map.put("doNotPrompt", "true"); diff --git a/jdk/test/sun/security/krb5/auto/CleanState.java b/jdk/test/sun/security/krb5/auto/CleanState.java index cc6e7e54322..316a730edfe 100644 --- a/jdk/test/sun/security/krb5/auto/CleanState.java +++ b/jdk/test/sun/security/krb5/auto/CleanState.java @@ -49,11 +49,11 @@ public class CleanState { final char[] password = OneKDC.PASS; char[] badpassword = "hellokitty".toCharArray(); - Map map = new HashMap(); + Map map = new HashMap<>(); map.put("useTicketCache", "false"); map.put("doNotPrompt", "false"); map.put("tryFirstPass", "true"); - Map shared = new HashMap(); + Map shared = new HashMap<>(); shared.put("javax.security.auth.login.name", name); shared.put("javax.security.auth.login.password", badpassword); diff --git a/jdk/test/sun/security/krb5/auto/Context.java b/jdk/test/sun/security/krb5/auto/Context.java index 86726e3f7b9..4c67109121f 100644 --- a/jdk/test/sun/security/krb5/auto/Context.java +++ b/jdk/test/sun/security/krb5/auto/Context.java @@ -117,8 +117,8 @@ public class Context { out.name = user; out.s = new Subject(); Krb5LoginModule krb5 = new Krb5LoginModule(); - Map map = new HashMap(); - Map shared = new HashMap(); + Map map = new HashMap<>(); + Map shared = new HashMap<>(); if (pass != null) { map.put("useFirstPass", "true"); @@ -151,7 +151,7 @@ public class Context { out.name = user; out.s = new Subject(); Krb5LoginModule krb5 = new Krb5LoginModule(); - Map map = new HashMap(); + Map map = new HashMap<>(); map.put("doNotPrompt", "true"); map.put("useTicketCache", "false"); diff --git a/jdk/test/sun/security/krb5/auto/HttpNegotiateServer.java b/jdk/test/sun/security/krb5/auto/HttpNegotiateServer.java index 96323b76553..e25e60d6ab5 100644 --- a/jdk/test/sun/security/krb5/auto/HttpNegotiateServer.java +++ b/jdk/test/sun/security/krb5/auto/HttpNegotiateServer.java @@ -297,8 +297,8 @@ public class HttpNegotiateServer { } Krb5LoginModule krb5 = new Krb5LoginModule(); - Map map = new HashMap(); - Map shared = new HashMap(); + Map map = new HashMap<>(); + Map shared = new HashMap<>(); map.put("storeKey", "true"); map.put("isInitiator", "false"); diff --git a/jdk/test/sun/security/krb5/auto/KDC.java b/jdk/test/sun/security/krb5/auto/KDC.java index 796415bb03e..36dc08b4ae6 100644 --- a/jdk/test/sun/security/krb5/auto/KDC.java +++ b/jdk/test/sun/security/krb5/auto/KDC.java @@ -132,7 +132,7 @@ public class KDC { // Principal db. principal -> pass. A case-insensitive TreeMap is used // so that even if the client provides a name with different case, the KDC // can still locate the principal and give back correct salt. - private TreeMap passwords = new TreeMap + private TreeMap passwords = new TreeMap<> (String.CASE_INSENSITIVE_ORDER); // Realm name @@ -142,9 +142,9 @@ public class KDC { // Service port number private int port; // The request/response job queue - private BlockingQueue q = new ArrayBlockingQueue(100); + private BlockingQueue q = new ArrayBlockingQueue<>(100); // Options - private Map options = new HashMap(); + private Map options = new HashMap<>(); private Thread thread1, thread2, thread3; DatagramSocket u1 = null; @@ -537,7 +537,7 @@ public class KDC { } } - private Map policies = new HashMap(); + private Map policies = new HashMap<>(); public void setPolicy(String rule, String value) { if (value == null) { @@ -760,7 +760,7 @@ public class KDC { private byte[] processAsReq(byte[] in) throws Exception { ASReq asReq = new ASReq(in); int[] eTypes = null; - List outPAs = new ArrayList(); + List outPAs = new ArrayList<>(); try { System.out.println(realm + "> " + asReq.reqBody.cname + diff --git a/jdk/test/sun/security/krb5/auto/LoginModuleOptions.java b/jdk/test/sun/security/krb5/auto/LoginModuleOptions.java index 44c4a414d0d..f22f774316f 100644 --- a/jdk/test/sun/security/krb5/auto/LoginModuleOptions.java +++ b/jdk/test/sun/security/krb5/auto/LoginModuleOptions.java @@ -135,8 +135,8 @@ public class LoginModuleOptions { throws Exception { Krb5LoginModule krb5 = new Krb5LoginModule(); Subject subject = new Subject(); - Map map = new HashMap(); - Map shared = new HashMap(); + Map map = new HashMap<>(); + Map shared = new HashMap<>(); int count = options.length / 2; for (int i = 0; i < count; i++) { diff --git a/jdk/test/sun/security/krb5/tools/KtabCheck.java b/jdk/test/sun/security/krb5/tools/KtabCheck.java index 0fe8b783168..e82b83ac740 100644 --- a/jdk/test/sun/security/krb5/tools/KtabCheck.java +++ b/jdk/test/sun/security/krb5/tools/KtabCheck.java @@ -39,7 +39,7 @@ public class KtabCheck { public static void main(String[] args) throws Exception { System.out.println("Checking " + Arrays.toString(args)); KeyTab ktab = KeyTab.getInstance(args[0]); - Set expected = new HashSet(); + Set expected = new HashSet<>(); for (int i=1; i Date: Thu, 13 Jan 2011 11:01:30 +0000 Subject: [PATCH 6/6] 6896088: URLClassLoader.close() apparently not working for JAR URLs on Windows Reviewed-by: chegar --- .../share/classes/sun/misc/URLClassPath.java | 18 ++- .../java/net/URLClassLoader/B6896088.java | 130 ++++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 jdk/test/java/net/URLClassLoader/B6896088.java diff --git a/jdk/src/share/classes/sun/misc/URLClassPath.java b/jdk/src/share/classes/sun/misc/URLClassPath.java index c828ab3e16e..d7664f1be7b 100644 --- a/jdk/src/share/classes/sun/misc/URLClassPath.java +++ b/jdk/src/share/classes/sun/misc/URLClassPath.java @@ -466,6 +466,7 @@ public class URLClassPath { */ private static class Loader implements Closeable { private final URL base; + private JarFile jarfile; // if this points to a jar file /* * Creates a new Loader for the specified URL. @@ -530,6 +531,17 @@ public class URLClassPath { } uc = url.openConnection(); InputStream in = uc.getInputStream(); + if (uc instanceof JarURLConnection) { + /* JarURLConnection.getInputStream() returns a separate + * instance on each call. So we have to close this here. + * The jar file cache will keep the file open. + * Also, need to remember the jar file so it can be closed + * in a hurry. + */ + JarURLConnection juc = (JarURLConnection)uc; + jarfile = juc.getJarFile(); + in.close(); + } } catch (Exception e) { return null; } @@ -559,7 +571,11 @@ public class URLClassPath { * close this loader and release all resources * method overridden in sub-classes */ - public void close () throws IOException {} + public void close () throws IOException { + if (jarfile != null) { + jarfile.close(); + } + } /* * Returns the local class path for this loader, or null if none. diff --git a/jdk/test/java/net/URLClassLoader/B6896088.java b/jdk/test/java/net/URLClassLoader/B6896088.java new file mode 100644 index 00000000000..4e0792b0345 --- /dev/null +++ b/jdk/test/java/net/URLClassLoader/B6896088.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 6896088 + * @run main/othervm B6896088 + * @summary URLClassLoader.close() apparently not working for JAR URLs on Windows + */ + +import java.net.*; +import java.io.*; +import java.nio.file.Path; + +public class B6896088 { + + public static void main(String[] args) throws Exception { + + String dir = System.getProperty ("test.classes"); + File jarf = new File (dir, "foo.jar"); + FileOutputStream fos = new FileOutputStream (jarf); + fos.write(bytes(nums)); + fos.close(); + + // Create URL using JAR protocol + String jarName = (jarf.toURI()).toString(); + URL url = new URL("jar", "", jarName + "!/"); + + // Create URLClassLoader from the URL + URLClassLoader loader = new URLClassLoader(new URL[]{url}); + Class c = loader.loadClass("Foo"); + + // Close the URLClassLoader so we can delete/update the jar file + loader.close(); + + // Now try to delete the jar file + + if (jarf.delete() && !jarf.exists()) { + System.out.println(jarf.getName()+" File Deleted"); + } else { + System.out.println(jarf.getName()+" File Not Deleted"); + throw new RuntimeException ("File not deleted"); + } + } + + static byte[] bytes (int[] i) { + byte[] buf = new byte [i.length]; + + for (int j=0; j