diff --git a/jdk/src/java.base/share/classes/java/util/Queue.java b/jdk/src/java.base/share/classes/java/util/Queue.java index 7d5e39c7030..e94b22c7fb2 100644 --- a/jdk/src/java.base/share/classes/java/util/Queue.java +++ b/jdk/src/java.base/share/classes/java/util/Queue.java @@ -124,7 +124,6 @@ package java.util; * always well-defined for queues with the same elements but different * ordering properties. * - * *

This interface is a member of the * * Java Collections Framework. diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java index d2392c62798..471316a9040 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java @@ -68,6 +68,7 @@ import java.util.function.ToIntFunction; import java.util.function.ToLongBiFunction; import java.util.function.ToLongFunction; import java.util.stream.Stream; +import jdk.internal.misc.Unsafe; /** * A hash table supporting full concurrency of retrievals and @@ -747,7 +748,7 @@ public class ConcurrentHashMap extends AbstractMap /* ---------------- Table element access -------------- */ /* - * Volatile access methods are used for table elements as well as + * Atomic access methods are used for table elements as well as * elements of in-progress next table while resizing. All uses of * the tab arguments must be null checked by callers. All callers * also paranoically precheck that tab's length is not zero (or an @@ -757,14 +758,12 @@ public class ConcurrentHashMap extends AbstractMap * errors by users, these checks must operate on local variables, * which accounts for some odd-looking inline assignments below. * Note that calls to setTabAt always occur within locked regions, - * and so in principle require only release ordering, not - * full volatile semantics, but are currently coded as volatile - * writes to be conservative. + * and so require only release ordering. */ @SuppressWarnings("unchecked") static final Node tabAt(Node[] tab, int i) { - return (Node)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE); + return (Node)U.getObjectAcquire(tab, ((long)i << ASHIFT) + ABASE); } static final boolean casTabAt(Node[] tab, int i, @@ -773,7 +772,7 @@ public class ConcurrentHashMap extends AbstractMap } static final void setTabAt(Node[] tab, int i, Node v) { - U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v); + U.putObjectRelease(tab, ((long)i << ASHIFT) + ABASE, v); } /* ---------------- Fields -------------- */ @@ -3298,7 +3297,7 @@ public class ConcurrentHashMap extends AbstractMap return true; } - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); + private static final Unsafe U = Unsafe.getUnsafe(); private static final long LOCKSTATE; static { try { @@ -6341,7 +6340,7 @@ public class ConcurrentHashMap extends AbstractMap } // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); + private static final Unsafe U = Unsafe.getUnsafe(); private static final long SIZECTL; private static final long TRANSFERINDEX; private static final long BASECOUNT; diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java index 698079066cb..e8193a00256 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.AbstractCollection; import java.util.Arrays; import java.util.Collection; @@ -292,64 +294,23 @@ public class ConcurrentLinkedDeque volatile Node prev; volatile E item; volatile Node next; + } - Node() { // default constructor for NEXT_TERMINATOR, PREV_TERMINATOR - } - - /** - * Constructs a new node. Uses relaxed write because item can - * only be seen after publication via casNext or casPrev. - */ - Node(E item) { - U.putObject(this, ITEM, item); - } - - boolean casItem(E cmp, E val) { - return U.compareAndSwapObject(this, ITEM, cmp, val); - } - - void lazySetNext(Node val) { - U.putObjectRelease(this, NEXT, val); - } - - boolean casNext(Node cmp, Node val) { - return U.compareAndSwapObject(this, NEXT, cmp, val); - } - - void lazySetPrev(Node val) { - U.putObjectRelease(this, PREV, val); - } - - boolean casPrev(Node cmp, Node val) { - return U.compareAndSwapObject(this, PREV, cmp, val); - } - - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long PREV; - private static final long ITEM; - private static final long NEXT; - - static { - try { - PREV = U.objectFieldOffset - (Node.class.getDeclaredField("prev")); - ITEM = U.objectFieldOffset - (Node.class.getDeclaredField("item")); - NEXT = U.objectFieldOffset - (Node.class.getDeclaredField("next")); - } catch (ReflectiveOperationException e) { - throw new Error(e); - } - } + /** + * Returns a new node holding item. Uses relaxed write because item + * can only be seen after piggy-backing publication via CAS. + */ + static Node newNode(E item) { + Node node = new Node(); + ITEM.set(node, item); + return node; } /** * Links e as first element. */ private void linkFirst(E e) { - final Node newNode = new Node(Objects.requireNonNull(e)); + final Node newNode = newNode(Objects.requireNonNull(e)); restartFromHead: for (;;) @@ -363,13 +324,13 @@ public class ConcurrentLinkedDeque continue restartFromHead; else { // p is first node - newNode.lazySetNext(p); // CAS piggyback - if (p.casPrev(null, newNode)) { + NEXT.set(newNode, p); // CAS piggyback + if (PREV.compareAndSet(p, null, newNode)) { // Successful CAS is the linearization point // for e to become an element of this deque, // and for newNode to become "live". - if (p != h) // hop two nodes at a time - casHead(h, newNode); // Failure is OK. + if (p != h) // hop two nodes at a time; failure is OK + HEAD.weakCompareAndSetVolatile(this, h, newNode); return; } // Lost CAS race to another thread; re-read prev @@ -381,7 +342,7 @@ public class ConcurrentLinkedDeque * Links e as last element. */ private void linkLast(E e) { - final Node newNode = new Node(Objects.requireNonNull(e)); + final Node newNode = newNode(Objects.requireNonNull(e)); restartFromTail: for (;;) @@ -395,13 +356,13 @@ public class ConcurrentLinkedDeque continue restartFromTail; else { // p is last node - newNode.lazySetPrev(p); // CAS piggyback - if (p.casNext(null, newNode)) { + PREV.set(newNode, p); // CAS piggyback + if (NEXT.compareAndSet(p, null, newNode)) { // Successful CAS is the linearization point // for e to become an element of this deque, // and for newNode to become "live". - if (p != t) // hop two nodes at a time - casTail(t, newNode); // Failure is OK. + if (p != t) // hop two nodes at a time; failure is OK + TAIL.weakCompareAndSetVolatile(this, t, newNode); return; } // Lost CAS race to another thread; re-read next @@ -516,8 +477,8 @@ public class ConcurrentLinkedDeque updateTail(); // Ensure x is not reachable from tail // Finally, actually gc-unlink - x.lazySetPrev(isFirst ? prevTerminator() : x); - x.lazySetNext(isLast ? nextTerminator() : x); + PREV.setRelease(x, isFirst ? prevTerminator() : x); + NEXT.setRelease(x, isLast ? nextTerminator() : x); } } } @@ -531,7 +492,8 @@ public class ConcurrentLinkedDeque // assert first.item == null; for (Node o = null, p = next, q;;) { if (p.item != null || (q = p.next) == null) { - if (o != null && p.prev != p && first.casNext(next, p)) { + if (o != null && p.prev != p && + NEXT.compareAndSet(first, next, p)) { skipDeletedPredecessors(p); if (first.prev == null && (p.next == null || p.item != null) && @@ -541,8 +503,8 @@ public class ConcurrentLinkedDeque updateTail(); // Ensure o is not reachable from tail // Finally, actually gc-unlink - o.lazySetNext(o); - o.lazySetPrev(prevTerminator()); + NEXT.setRelease(o, o); + PREV.setRelease(o, prevTerminator()); } } return; @@ -565,7 +527,8 @@ public class ConcurrentLinkedDeque // assert last.item == null; for (Node o = null, p = prev, q;;) { if (p.item != null || (q = p.prev) == null) { - if (o != null && p.next != p && last.casPrev(prev, p)) { + if (o != null && p.next != p && + PREV.compareAndSet(last, prev, p)) { skipDeletedSuccessors(p); if (last.next == null && (p.prev == null || p.item != null) && @@ -575,8 +538,8 @@ public class ConcurrentLinkedDeque updateTail(); // Ensure o is not reachable from tail // Finally, actually gc-unlink - o.lazySetPrev(o); - o.lazySetNext(nextTerminator()); + PREV.setRelease(o, o); + NEXT.setRelease(o, nextTerminator()); } } return; @@ -607,7 +570,7 @@ public class ConcurrentLinkedDeque (q = (p = q).prev) == null) { // It is possible that p is PREV_TERMINATOR, // but if so, the CAS is guaranteed to fail. - if (casHead(h, p)) + if (HEAD.compareAndSet(this, h, p)) return; else continue restartFromHead; @@ -637,7 +600,7 @@ public class ConcurrentLinkedDeque (q = (p = q).next) == null) { // It is possible that p is NEXT_TERMINATOR, // but if so, the CAS is guaranteed to fail. - if (casTail(t, p)) + if (TAIL.compareAndSet(this, t, p)) return; else continue restartFromTail; @@ -675,7 +638,7 @@ public class ConcurrentLinkedDeque } // found active CAS target - if (prev == p || x.casPrev(prev, p)) + if (prev == p || PREV.compareAndSet(x, prev, p)) return; } while (x.item != null || x.next == null); @@ -706,7 +669,7 @@ public class ConcurrentLinkedDeque } // found active CAS target - if (next == p || x.casNext(next, p)) + if (next == p || NEXT.compareAndSet(x, next, p)) return; } while (x.item != null || x.prev == null); @@ -751,7 +714,7 @@ public class ConcurrentLinkedDeque else if (p == h // It is possible that p is PREV_TERMINATOR, // but if so, the CAS is guaranteed to fail. - || casHead(h, p)) + || HEAD.compareAndSet(this, h, p)) return p; else continue restartFromHead; @@ -776,7 +739,7 @@ public class ConcurrentLinkedDeque else if (p == t // It is possible that p is NEXT_TERMINATOR, // but if so, the CAS is guaranteed to fail. - || casTail(t, p)) + || TAIL.compareAndSet(this, t, p)) return p; else continue restartFromTail; @@ -802,7 +765,7 @@ public class ConcurrentLinkedDeque * Constructs an empty deque. */ public ConcurrentLinkedDeque() { - head = tail = new Node(null); + head = tail = new Node(); } /** @@ -818,12 +781,12 @@ public class ConcurrentLinkedDeque // Copy c into a private chain of Nodes Node h = null, t = null; for (E e : c) { - Node newNode = new Node(Objects.requireNonNull(e)); + Node newNode = newNode(Objects.requireNonNull(e)); if (h == null) h = t = newNode; else { - t.lazySetNext(newNode); - newNode.lazySetPrev(t); + NEXT.set(t, newNode); + PREV.set(newNode, t); t = newNode; } } @@ -836,12 +799,12 @@ public class ConcurrentLinkedDeque private void initHeadTail(Node h, Node t) { if (h == t) { if (h == null) - h = t = new Node(null); + h = t = new Node(); else { // Avoid edge case of a single Node with non-null item. - Node newNode = new Node(null); - t.lazySetNext(newNode); - newNode.lazySetPrev(t); + Node newNode = new Node(); + NEXT.set(t, newNode); + PREV.set(newNode, t); t = newNode; } } @@ -934,7 +897,7 @@ public class ConcurrentLinkedDeque public E pollFirst() { for (Node p = first(); p != null; p = succ(p)) { E item = p.item; - if (item != null && p.casItem(item, null)) { + if (item != null && ITEM.compareAndSet(p, item, null)) { unlink(p); return item; } @@ -945,7 +908,7 @@ public class ConcurrentLinkedDeque public E pollLast() { for (Node p = last(); p != null; p = pred(p)) { E item = p.item; - if (item != null && p.casItem(item, null)) { + if (item != null && ITEM.compareAndSet(p, item, null)) { unlink(p); return item; } @@ -1031,7 +994,8 @@ public class ConcurrentLinkedDeque Objects.requireNonNull(o); for (Node p = first(); p != null; p = succ(p)) { E item = p.item; - if (item != null && o.equals(item) && p.casItem(item, null)) { + if (item != null && o.equals(item) && + ITEM.compareAndSet(p, item, null)) { unlink(p); return true; } @@ -1055,7 +1019,8 @@ public class ConcurrentLinkedDeque Objects.requireNonNull(o); for (Node p = last(); p != null; p = pred(p)) { E item = p.item; - if (item != null && o.equals(item) && p.casItem(item, null)) { + if (item != null && o.equals(item) && + ITEM.compareAndSet(p, item, null)) { unlink(p); return true; } @@ -1159,12 +1124,12 @@ public class ConcurrentLinkedDeque // Copy c into a private chain of Nodes Node beginningOfTheEnd = null, last = null; for (E e : c) { - Node newNode = new Node(Objects.requireNonNull(e)); + Node newNode = newNode(Objects.requireNonNull(e)); if (beginningOfTheEnd == null) beginningOfTheEnd = last = newNode; else { - last.lazySetNext(newNode); - newNode.lazySetPrev(last); + NEXT.set(last, newNode); + PREV.set(newNode, last); last = newNode; } } @@ -1184,16 +1149,16 @@ public class ConcurrentLinkedDeque continue restartFromTail; else { // p is last node - beginningOfTheEnd.lazySetPrev(p); // CAS piggyback - if (p.casNext(null, beginningOfTheEnd)) { + PREV.set(beginningOfTheEnd, p); // CAS piggyback + if (NEXT.compareAndSet(p, null, beginningOfTheEnd)) { // Successful CAS is the linearization point // for all elements to be added to this deque. - if (!casTail(t, last)) { + if (!TAIL.weakCompareAndSetVolatile(this, t, last)) { // Try a little harder to update tail, // since we may be adding many elements. t = tail; if (last.next == null) - casTail(t, last); + TAIL.weakCompareAndSetVolatile(this, t, last); } return true; } @@ -1586,41 +1551,38 @@ public class ConcurrentLinkedDeque Node h = null, t = null; for (Object item; (item = s.readObject()) != null; ) { @SuppressWarnings("unchecked") - Node newNode = new Node((E) item); + Node newNode = newNode((E) item); if (h == null) h = t = newNode; else { - t.lazySetNext(newNode); - newNode.lazySetPrev(t); + NEXT.set(t, newNode); + PREV.set(newNode, t); t = newNode; } } initHeadTail(h, t); } - private boolean casHead(Node cmp, Node val) { - return U.compareAndSwapObject(this, HEAD, cmp, val); - } - - private boolean casTail(Node cmp, Node val) { - return U.compareAndSwapObject(this, TAIL, cmp, val); - } - - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long HEAD; - private static final long TAIL; + // VarHandle mechanics + private static final VarHandle HEAD; + private static final VarHandle TAIL; + private static final VarHandle PREV; + private static final VarHandle NEXT; + private static final VarHandle ITEM; static { PREV_TERMINATOR = new Node(); PREV_TERMINATOR.next = PREV_TERMINATOR; NEXT_TERMINATOR = new Node(); NEXT_TERMINATOR.prev = NEXT_TERMINATOR; try { - HEAD = U.objectFieldOffset - (ConcurrentLinkedDeque.class.getDeclaredField("head")); - TAIL = U.objectFieldOffset - (ConcurrentLinkedDeque.class.getDeclaredField("tail")); + MethodHandles.Lookup l = MethodHandles.lookup(); + HEAD = l.findVarHandle(ConcurrentLinkedDeque.class, "head", + Node.class); + TAIL = l.findVarHandle(ConcurrentLinkedDeque.class, "tail", + Node.class); + PREV = l.findVarHandle(Node.class, "prev", Node.class); + NEXT = l.findVarHandle(Node.class, "next", Node.class); + ITEM = l.findVarHandle(Node.class, "item", Object.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java index ae246a4e780..1e3f1aad4f6 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.AbstractQueue; import java.util.Arrays; import java.util.Collection; @@ -166,9 +168,8 @@ public class ConcurrentLinkedQueue extends AbstractQueue * this is merely an optimization. * * When constructing a Node (before enqueuing it) we avoid paying - * for a volatile write to item by using Unsafe.putObject instead - * of a normal write. This allows the cost of enqueue to be - * "one-and-a-half" CASes. + * for a volatile write to item. This allows the cost of enqueue + * to be "one-and-a-half" CASes. * * Both head and tail may or may not point to a Node with a * non-null item. If the queue is empty, all items must of course @@ -178,33 +179,21 @@ public class ConcurrentLinkedQueue extends AbstractQueue * optimization. */ - private static class Node { + static final class Node { volatile E item; volatile Node next; } /** * Returns a new node holding item. Uses relaxed write because item - * can only be seen after piggy-backing publication via casNext. + * can only be seen after piggy-backing publication via CAS. */ static Node newNode(E item) { Node node = new Node(); - U.putObject(node, ITEM, item); + ITEM.set(node, item); return node; } - static boolean casItem(Node node, E cmp, E val) { - return U.compareAndSwapObject(node, ITEM, cmp, val); - } - - static void lazySetNext(Node node, Node val) { - U.putObjectRelease(node, NEXT, val); - } - - static boolean casNext(Node node, Node cmp, Node val) { - return U.compareAndSwapObject(node, NEXT, cmp, val); - } - /** * A node from which the first live (non-deleted) node (if any) * can be reached in O(1) time. @@ -256,7 +245,7 @@ public class ConcurrentLinkedQueue extends AbstractQueue if (h == null) h = t = newNode; else { - lazySetNext(t, newNode); + NEXT.set(t, newNode); t = newNode; } } @@ -286,8 +275,8 @@ public class ConcurrentLinkedQueue extends AbstractQueue */ final void updateHead(Node h, Node p) { // assert h != null && p != null && (h == p || h.item == null); - if (h != p && casHead(h, p)) - lazySetNext(h, h); + if (h != p && HEAD.compareAndSet(this, h, p)) + NEXT.setRelease(h, h); } /** @@ -314,12 +303,12 @@ public class ConcurrentLinkedQueue extends AbstractQueue Node q = p.next; if (q == null) { // p is last node - if (casNext(p, null, newNode)) { + if (NEXT.compareAndSet(p, null, newNode)) { // Successful CAS is the linearization point // for e to become an element of this queue, // and for newNode to become "live". - if (p != t) // hop two nodes at a time - casTail(t, newNode); // Failure is OK. + if (p != t) // hop two nodes at a time; failure is OK + TAIL.weakCompareAndSetVolatile(this, t, newNode); return true; } // Lost CAS race to another thread; re-read next @@ -342,7 +331,7 @@ public class ConcurrentLinkedQueue extends AbstractQueue for (Node h = head, p = h, q;;) { E item = p.item; - if (item != null && casItem(p, item, null)) { + if (item != null && ITEM.compareAndSet(p, item, null)) { // Successful CAS is the linearization point // for item to be removed from this queue. if (p != h) // hop two nodes at a time @@ -483,12 +472,12 @@ public class ConcurrentLinkedQueue extends AbstractQueue next = succ(p); continue; } - removed = casItem(p, item, null); + removed = ITEM.compareAndSet(p, item, null); } next = succ(p); if (pred != null && next != null) // unlink - casNext(pred, p, next); + NEXT.weakCompareAndSetVolatile(pred, p, next); if (removed) return true; } @@ -520,7 +509,7 @@ public class ConcurrentLinkedQueue extends AbstractQueue if (beginningOfTheEnd == null) beginningOfTheEnd = last = newNode; else { - lazySetNext(last, newNode); + NEXT.set(last, newNode); last = newNode; } } @@ -532,15 +521,15 @@ public class ConcurrentLinkedQueue extends AbstractQueue Node q = p.next; if (q == null) { // p is last node - if (casNext(p, null, beginningOfTheEnd)) { + if (NEXT.compareAndSet(p, null, beginningOfTheEnd)) { // Successful CAS is the linearization point // for all elements to be added to this queue. - if (!casTail(t, last)) { + if (!TAIL.weakCompareAndSetVolatile(this, t, last)) { // Try a little harder to update tail, // since we may be adding many elements. t = tail; if (last.next == null) - casTail(t, last); + TAIL.weakCompareAndSetVolatile(this, t, last); } return true; } @@ -744,7 +733,7 @@ public class ConcurrentLinkedQueue extends AbstractQueue } // unlink deleted nodes if ((q = succ(p)) != null) - casNext(pred, p, q); + NEXT.compareAndSet(pred, p, q); } } @@ -801,7 +790,7 @@ public class ConcurrentLinkedQueue extends AbstractQueue if (h == null) h = t = newNode; else { - lazySetNext(t, newNode); + NEXT.set(t, newNode); t = newNode; } } @@ -919,31 +908,20 @@ public class ConcurrentLinkedQueue extends AbstractQueue return new CLQSpliterator(this); } - private boolean casTail(Node cmp, Node val) { - return U.compareAndSwapObject(this, TAIL, cmp, val); - } - - private boolean casHead(Node cmp, Node val) { - return U.compareAndSwapObject(this, HEAD, cmp, val); - } - - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long HEAD; - private static final long TAIL; - private static final long ITEM; - private static final long NEXT; + // VarHandle mechanics + private static final VarHandle HEAD; + private static final VarHandle TAIL; + private static final VarHandle ITEM; + private static final VarHandle NEXT; static { try { - HEAD = U.objectFieldOffset - (ConcurrentLinkedQueue.class.getDeclaredField("head")); - TAIL = U.objectFieldOffset - (ConcurrentLinkedQueue.class.getDeclaredField("tail")); - ITEM = U.objectFieldOffset - (Node.class.getDeclaredField("item")); - NEXT = U.objectFieldOffset - (Node.class.getDeclaredField("next")); + MethodHandles.Lookup l = MethodHandles.lookup(); + HEAD = l.findVarHandle(ConcurrentLinkedQueue.class, "head", + Node.class); + TAIL = l.findVarHandle(ConcurrentLinkedQueue.class, "tail", + Node.class); + ITEM = l.findVarHandle(Node.class, "item", Object.class); + NEXT = l.findVarHandle(Node.class, "next", Node.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java index fa53ded9710..7734a265bed 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.io.Serializable; import java.util.AbstractCollection; import java.util.AbstractMap; @@ -401,7 +403,7 @@ public class ConcurrentSkipListMap extends AbstractMap * compareAndSet head node. */ private boolean casHead(HeadIndex cmp, HeadIndex val) { - return U.compareAndSwapObject(this, HEAD, cmp, val); + return HEAD.compareAndSet(this, cmp, val); } /* ---------------- Nodes -------------- */ @@ -444,14 +446,14 @@ public class ConcurrentSkipListMap extends AbstractMap * compareAndSet value field. */ boolean casValue(Object cmp, Object val) { - return U.compareAndSwapObject(this, VALUE, cmp, val); + return VALUE.compareAndSet(this, cmp, val); } /** * compareAndSet next field. */ boolean casNext(Node cmp, Node val) { - return U.compareAndSwapObject(this, NEXT, cmp, val); + return NEXT.compareAndSet(this, cmp, val); } /** @@ -532,20 +534,16 @@ public class ConcurrentSkipListMap extends AbstractMap return new AbstractMap.SimpleImmutableEntry(key, vv); } - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long VALUE; - private static final long NEXT; - + // VarHandle mechanics + private static final VarHandle VALUE; + private static final VarHandle NEXT; static { try { - VALUE = U.objectFieldOffset - (Node.class.getDeclaredField("value")); - NEXT = U.objectFieldOffset - (Node.class.getDeclaredField("next")); + MethodHandles.Lookup l = MethodHandles.lookup(); + VALUE = l.findVarHandle(Node.class, "value", Object.class); + NEXT = l.findVarHandle(Node.class, "next", Node.class); } catch (ReflectiveOperationException e) { - throw new Error(e); + throw new Error(e); } } } @@ -577,7 +575,7 @@ public class ConcurrentSkipListMap extends AbstractMap * compareAndSet right field. */ final boolean casRight(Index cmp, Index val) { - return U.compareAndSwapObject(this, RIGHT, cmp, val); + return RIGHT.compareAndSet(this, cmp, val); } /** @@ -613,13 +611,12 @@ public class ConcurrentSkipListMap extends AbstractMap return node.value != null && casRight(succ, succ.right); } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long RIGHT; + // VarHandle mechanics + private static final VarHandle RIGHT; static { try { - RIGHT = U.objectFieldOffset - (Index.class.getDeclaredField("right")); + MethodHandles.Lookup l = MethodHandles.lookup(); + RIGHT = l.findVarHandle(Index.class, "right", Index.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -3607,13 +3604,13 @@ public class ConcurrentSkipListMap extends AbstractMap } } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long HEAD; + // VarHandle mechanics + private static final VarHandle HEAD; static { try { - HEAD = U.objectFieldOffset - (ConcurrentSkipListMap.class.getDeclaredField("head")); + MethodHandles.Lookup l = MethodHandles.lookup(); + HEAD = l.findVarHandle(ConcurrentSkipListMap.class, "head", + HeadIndex.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java index 2ecacf8e185..45bde4215c5 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; @@ -507,15 +509,16 @@ public class ConcurrentSkipListSet // Support for resetting map in clone private void setMap(ConcurrentNavigableMap map) { - U.putObjectVolatile(this, MAP, map); + MAP.setVolatile(this, map); } - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long MAP; + // VarHandle mechanics + private static final VarHandle MAP; static { try { - MAP = U.objectFieldOffset - (ConcurrentSkipListSet.class.getDeclaredField("m")); + MethodHandles.Lookup l = MethodHandles.lookup(); + MAP = l.findVarHandle(ConcurrentSkipListSet.class, "m", + ConcurrentNavigableMap.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java b/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java index 65baf7c87c8..1f1b83cbfca 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java @@ -34,6 +34,7 @@ package java.util.concurrent; +import java.lang.reflect.Field; import java.util.AbstractList; import java.util.Arrays; import java.util.Collection; @@ -1541,17 +1542,21 @@ public class CopyOnWriteArrayList } } - // Support for resetting lock while deserializing + /** Initializes the lock; for use when deserializing or cloning. */ private void resetLock() { - U.putObjectVolatile(this, LOCK, new Object()); - } - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long LOCK; - static { + Field lockField = java.security.AccessController.doPrivileged( + (java.security.PrivilegedAction) () -> { + try { + Field f = CopyOnWriteArrayList.class + .getDeclaredField("lock"); + f.setAccessible(true); + return f; + } catch (ReflectiveOperationException e) { + throw new Error(e); + }}); try { - LOCK = U.objectFieldOffset - (CopyOnWriteArrayList.class.getDeclaredField("lock")); - } catch (ReflectiveOperationException e) { + lockField.set(this, new Object()); + } catch (IllegalAccessException e) { throw new Error(e); } } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java b/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java index 1e56b09dfc1..a61762b5669 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java @@ -35,6 +35,9 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; + /** * A {@link ForkJoinTask} with a completion action performed when * triggered and there are no remaining pending actions. @@ -524,7 +527,7 @@ public abstract class CountedCompleter extends ForkJoinTask { * @param delta the value to add */ public final void addToPendingCount(int delta) { - U.getAndAddInt(this, PENDING, delta); + PENDING.getAndAdd(this, delta); } /** @@ -536,7 +539,7 @@ public abstract class CountedCompleter extends ForkJoinTask { * @return {@code true} if successful */ public final boolean compareAndSetPendingCount(int expected, int count) { - return U.compareAndSwapInt(this, PENDING, expected, count); + return PENDING.compareAndSet(this, expected, count); } /** @@ -548,7 +551,7 @@ public abstract class CountedCompleter extends ForkJoinTask { public final int decrementPendingCountUnlessZero() { int c; do {} while ((c = pending) != 0 && - !U.compareAndSwapInt(this, PENDING, c, c - 1)); + !PENDING.weakCompareAndSetVolatile(this, c, c - 1)); return c; } @@ -581,7 +584,7 @@ public abstract class CountedCompleter extends ForkJoinTask { return; } } - else if (U.compareAndSwapInt(a, PENDING, c, c - 1)) + else if (PENDING.weakCompareAndSetVolatile(a, c, c - 1)) return; } } @@ -604,7 +607,7 @@ public abstract class CountedCompleter extends ForkJoinTask { return; } } - else if (U.compareAndSwapInt(a, PENDING, c, c - 1)) + else if (PENDING.weakCompareAndSetVolatile(a, c, c - 1)) return; } } @@ -649,7 +652,7 @@ public abstract class CountedCompleter extends ForkJoinTask { for (int c;;) { if ((c = pending) == 0) return this; - else if (U.compareAndSwapInt(this, PENDING, c, c - 1)) + else if (PENDING.weakCompareAndSetVolatile(this, c, c - 1)) return null; } } @@ -753,13 +756,13 @@ public abstract class CountedCompleter extends ForkJoinTask { */ protected void setRawResult(T t) { } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long PENDING; + // VarHandle mechanics + private static final VarHandle PENDING; static { try { - PENDING = U.objectFieldOffset - (CountedCompleter.class.getDeclaredField("pending")); + MethodHandles.Lookup l = MethodHandles.lookup(); + PENDING = l.findVarHandle(CountedCompleter.class, "pending", int.class); + } catch (ReflectiveOperationException e) { throw new Error(e); } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java b/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java index 069d6e0f871..8dcfe9bf1df 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java @@ -36,6 +36,10 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.util.concurrent.locks.LockSupport; + /** * A synchronization point at which threads can pair and swap elements * within pairs. Each thread presents some object on entry to the @@ -155,9 +159,7 @@ public class Exchanger { * a value that is enough for common platforms. Additionally, * extra care elsewhere is taken to avoid other false/unintended * sharing and to enhance locality, including adding padding (via - * @Contended) to Nodes, embedding "bound" as an Exchanger field, - * and reworking some park/unpark mechanics compared to - * LockSupport versions. + * @Contended) to Nodes, embedding "bound" as an Exchanger field. * * The arena starts out with only one used slot. We expand the * effective arena size by tracking collisions; i.e., failed CASes @@ -234,12 +236,12 @@ public class Exchanger { * because most of the logic relies on reads of fields that are * maintained as local variables so can't be nicely factored -- * mainly, here, bulky spin->yield->block/cancel code), and - * heavily dependent on intrinsics (Unsafe) to use inlined + * heavily dependent on intrinsics (VarHandles) to use inlined * embedded CAS and related memory access operations (that tend * not to be as readily inlined by dynamic compilers when they are * hidden behind other methods that would more nicely name and * encapsulate the intended effects). This includes the use of - * putXRelease to clear fields of the per-thread Nodes between + * setRelease to clear fields of the per-thread Nodes between * uses. Note that field Node.item is not declared as volatile * even though it is read by releasing threads, because they only * do so after CAS operations that must precede access, and all @@ -252,10 +254,10 @@ public class Exchanger { */ /** - * The byte distance (as a shift value) between any two used slots - * in the arena. 1 << ASHIFT should be at least cacheline size. + * The index distance (as a shift value) between any two used slots + * in the arena, spacing them out to avoid false sharing. */ - private static final int ASHIFT = 7; + private static final int ASHIFT = 5; /** * The maximum supported arena index. The maximum allocatable @@ -356,27 +358,31 @@ public class Exchanger { */ private final Object arenaExchange(Object item, boolean timed, long ns) { Node[] a = arena; + int alen = a.length; Node p = participant.get(); for (int i = p.index;;) { // access slot at i - int b, m, c; long j; // j is raw array offset - Node q = (Node)U.getObjectVolatile(a, j = (i << ASHIFT) + ABASE); - if (q != null && U.compareAndSwapObject(a, j, q, null)) { + int b, m, c; + int j = (i << ASHIFT) + ((1 << ASHIFT) - 1); + if (j < 0 || j >= alen) + j = alen - 1; + Node q = (Node)AA.getAcquire(a, j); + if (q != null && AA.compareAndSet(a, j, q, null)) { Object v = q.item; // release q.match = item; Thread w = q.parked; if (w != null) - U.unpark(w); + LockSupport.unpark(w); return v; } else if (i <= (m = (b = bound) & MMASK) && q == null) { p.item = item; // offer - if (U.compareAndSwapObject(a, j, null, p)) { + if (AA.compareAndSet(a, j, null, p)) { long end = (timed && m == 0) ? System.nanoTime() + ns : 0L; Thread t = Thread.currentThread(); // wait for (int h = p.hash, spins = SPINS;;) { Object v = p.match; if (v != null) { - U.putObjectRelease(p, MATCH, null); + MATCH.setRelease(p, null); p.item = null; // clear for next use p.hash = h; return v; @@ -389,22 +395,24 @@ public class Exchanger { (--spins & ((SPINS >>> 1) - 1)) == 0) Thread.yield(); // two yields per wait } - else if (U.getObjectVolatile(a, j) != p) + else if (AA.getAcquire(a, j) != p) spins = SPINS; // releaser hasn't set match yet else if (!t.isInterrupted() && m == 0 && (!timed || (ns = end - System.nanoTime()) > 0L)) { - U.putObject(t, BLOCKER, this); // emulate LockSupport p.parked = t; // minimize window - if (U.getObjectVolatile(a, j) == p) - U.park(false, ns); + if (AA.getAcquire(a, j) == p) { + if (ns == 0L) + LockSupport.park(this); + else + LockSupport.parkNanos(this, ns); + } p.parked = null; - U.putObject(t, BLOCKER, null); } - else if (U.getObjectVolatile(a, j) == p && - U.compareAndSwapObject(a, j, p, null)) { + else if (AA.getAcquire(a, j) == p && + AA.compareAndSet(a, j, p, null)) { if (m != 0) // try to shrink - U.compareAndSwapInt(this, BOUND, b, b + SEQ - 1); + BOUND.compareAndSet(this, b, b + SEQ - 1); p.item = null; p.hash = h; i = p.index >>>= 1; // descend @@ -426,7 +434,7 @@ public class Exchanger { i = (i != m || m == 0) ? m : m - 1; } else if ((c = p.collides) < m || m == FULL || - !U.compareAndSwapInt(this, BOUND, b, b + SEQ + 1)) { + !BOUND.compareAndSet(this, b, b + SEQ + 1)) { p.collides = c + 1; i = (i == 0) ? m : i - 1; // cyclically traverse } @@ -455,24 +463,24 @@ public class Exchanger { for (Node q;;) { if ((q = slot) != null) { - if (U.compareAndSwapObject(this, SLOT, q, null)) { + if (SLOT.compareAndSet(this, q, null)) { Object v = q.item; q.match = item; Thread w = q.parked; if (w != null) - U.unpark(w); + LockSupport.unpark(w); return v; } // create arena on contention, but continue until slot null if (NCPU > 1 && bound == 0 && - U.compareAndSwapInt(this, BOUND, 0, SEQ)) + BOUND.compareAndSet(this, 0, SEQ)) arena = new Node[(FULL + 2) << ASHIFT]; } else if (arena != null) return null; // caller must reroute to arenaExchange else { p.item = item; - if (U.compareAndSwapObject(this, SLOT, null, p)) + if (SLOT.compareAndSet(this, null, p)) break; p.item = null; } @@ -495,19 +503,21 @@ public class Exchanger { spins = SPINS; else if (!t.isInterrupted() && arena == null && (!timed || (ns = end - System.nanoTime()) > 0L)) { - U.putObject(t, BLOCKER, this); p.parked = t; - if (slot == p) - U.park(false, ns); + if (slot == p) { + if (ns == 0L) + LockSupport.park(this); + else + LockSupport.parkNanos(this, ns); + } p.parked = null; - U.putObject(t, BLOCKER, null); } - else if (U.compareAndSwapObject(this, SLOT, p, null)) { + else if (SLOT.compareAndSet(this, p, null)) { v = timed && ns <= 0L && !t.isInterrupted() ? TIMED_OUT : null; break; } } - U.putObjectRelease(p, MATCH, null); + MATCH.setRelease(p, null); p.item = null; p.hash = h; return v; @@ -556,8 +566,9 @@ public class Exchanger { @SuppressWarnings("unchecked") public V exchange(V x) throws InterruptedException { Object v; + Node[] a; Object item = (x == null) ? NULL_ITEM : x; // translate null args - if ((arena != null || + if (((a = arena) != null || (v = slotExchange(item, false, 0L)) == null) && ((Thread.interrupted() || // disambiguates null return (v = arenaExchange(item, false, 0L)) == null))) @@ -623,31 +634,18 @@ public class Exchanger { return (v == NULL_ITEM) ? null : (V)v; } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long BOUND; - private static final long SLOT; - private static final long MATCH; - private static final long BLOCKER; - private static final int ABASE; + // VarHandle mechanics + private static final VarHandle BOUND; + private static final VarHandle SLOT; + private static final VarHandle MATCH; + private static final VarHandle AA; static { try { - BOUND = U.objectFieldOffset - (Exchanger.class.getDeclaredField("bound")); - SLOT = U.objectFieldOffset - (Exchanger.class.getDeclaredField("slot")); - - MATCH = U.objectFieldOffset - (Node.class.getDeclaredField("match")); - - BLOCKER = U.objectFieldOffset - (Thread.class.getDeclaredField("parkBlocker")); - - int scale = U.arrayIndexScale(Node[].class); - if ((scale & (scale - 1)) != 0 || scale > (1 << ASHIFT)) - throw new Error("Unsupported array scale"); - // ABASE absorbs padding in front of element 0 - ABASE = U.arrayBaseOffset(Node[].class) + (1 << ASHIFT); + MethodHandles.Lookup l = MethodHandles.lookup(); + BOUND = l.findVarHandle(Exchanger.class, "bound", int.class); + SLOT = l.findVarHandle(Exchanger.class, "slot", Node.class); + MATCH = l.findVarHandle(Node.class, "match", Object.class); + AA = MethodHandles.arrayElementVarHandle(Node[].class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java b/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java index aade083ffe3..82e773c7630 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.concurrent.locks.LockSupport; /** @@ -69,9 +71,6 @@ public class FutureTask implements RunnableFuture { * cancellation races. Sync control in the current design relies * on a "state" field updated via CAS to track completion, along * with a simple Treiber stack to hold waiting threads. - * - * Style note: As usual, we bypass overhead of using - * AtomicXFieldUpdaters and instead directly use Unsafe intrinsics. */ /** @@ -163,9 +162,8 @@ public class FutureTask implements RunnableFuture { } public boolean cancel(boolean mayInterruptIfRunning) { - if (!(state == NEW && - U.compareAndSwapInt(this, STATE, NEW, - mayInterruptIfRunning ? INTERRUPTING : CANCELLED))) + if (!(state == NEW && STATE.compareAndSet + (this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED))) return false; try { // in case call to interrupt throws exception if (mayInterruptIfRunning) { @@ -174,7 +172,7 @@ public class FutureTask implements RunnableFuture { if (t != null) t.interrupt(); } finally { // final state - U.putIntRelease(this, STATE, INTERRUPTED); + STATE.setRelease(this, INTERRUPTED); } } } finally { @@ -228,9 +226,9 @@ public class FutureTask implements RunnableFuture { * @param v the value */ protected void set(V v) { - if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) { + if (STATE.compareAndSet(this, NEW, COMPLETING)) { outcome = v; - U.putIntRelease(this, STATE, NORMAL); // final state + STATE.setRelease(this, NORMAL); // final state finishCompletion(); } } @@ -246,16 +244,16 @@ public class FutureTask implements RunnableFuture { * @param t the cause of failure */ protected void setException(Throwable t) { - if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) { + if (STATE.compareAndSet(this, NEW, COMPLETING)) { outcome = t; - U.putIntRelease(this, STATE, EXCEPTIONAL); // final state + STATE.setRelease(this, EXCEPTIONAL); // final state finishCompletion(); } } public void run() { if (state != NEW || - !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread())) + !RUNNER.compareAndSet(this, null, Thread.currentThread())) return; try { Callable c = callable; @@ -296,7 +294,7 @@ public class FutureTask implements RunnableFuture { */ protected boolean runAndReset() { if (state != NEW || - !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread())) + !RUNNER.compareAndSet(this, null, Thread.currentThread())) return false; boolean ran = false; int s = state; @@ -363,7 +361,7 @@ public class FutureTask implements RunnableFuture { private void finishCompletion() { // assert state > COMPLETING; for (WaitNode q; (q = waiters) != null;) { - if (U.compareAndSwapObject(this, WAITERS, q, null)) { + if (WAITERS.weakCompareAndSetVolatile(this, q, null)) { for (;;) { Thread t = q.thread; if (t != null) { @@ -425,8 +423,7 @@ public class FutureTask implements RunnableFuture { q = new WaitNode(); } else if (!queued) - queued = U.compareAndSwapObject(this, WAITERS, - q.next = waiters, q); + queued = WAITERS.weakCompareAndSetVolatile(this, q.next = waiters, q); else if (timed) { final long parkNanos; if (startTime == 0L) { // first time @@ -475,7 +472,7 @@ public class FutureTask implements RunnableFuture { if (pred.thread == null) // check for race continue retry; } - else if (!U.compareAndSwapObject(this, WAITERS, q, s)) + else if (!WAITERS.compareAndSet(this, q, s)) continue retry; } break; @@ -483,19 +480,16 @@ public class FutureTask implements RunnableFuture { } } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long STATE; - private static final long RUNNER; - private static final long WAITERS; + // VarHandle mechanics + private static final VarHandle STATE; + private static final VarHandle RUNNER; + private static final VarHandle WAITERS; static { try { - STATE = U.objectFieldOffset - (FutureTask.class.getDeclaredField("state")); - RUNNER = U.objectFieldOffset - (FutureTask.class.getDeclaredField("runner")); - WAITERS = U.objectFieldOffset - (FutureTask.class.getDeclaredField("waiters")); + MethodHandles.Lookup l = MethodHandles.lookup(); + STATE = l.findVarHandle(FutureTask.class, "state", int.class); + RUNNER = l.findVarHandle(FutureTask.class, "runner", Thread.class); + WAITERS = l.findVarHandle(FutureTask.class, "waiters", WaitNode.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java b/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java index 69b9a694f85..cb613e3f092 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.AbstractQueue; import java.util.Arrays; import java.util.Collection; @@ -444,7 +446,7 @@ public class LinkedTransferQueue extends AbstractQueue /** * Queue nodes. Uses Object, not E, for items to allow forgetting - * them after use. Relies heavily on Unsafe mechanics to minimize + * them after use. Relies heavily on VarHandles to minimize * unnecessary ordering constraints: Writes that are intrinsically * ordered wrt other accesses or CASes use simple relaxed forms. */ @@ -456,12 +458,12 @@ public class LinkedTransferQueue extends AbstractQueue // CAS methods for fields final boolean casNext(Node cmp, Node val) { - return U.compareAndSwapObject(this, NEXT, cmp, val); + return NEXT.compareAndSet(this, cmp, val); } final boolean casItem(Object cmp, Object val) { // assert cmp == null || cmp.getClass() != Node.class; - return U.compareAndSwapObject(this, ITEM, cmp, val); + return ITEM.compareAndSet(this, cmp, val); } /** @@ -469,7 +471,7 @@ public class LinkedTransferQueue extends AbstractQueue * only be seen after publication via casNext. */ Node(Object item, boolean isData) { - U.putObject(this, ITEM, item); // relaxed write + ITEM.set(this, item); // relaxed write this.isData = isData; } @@ -478,7 +480,7 @@ public class LinkedTransferQueue extends AbstractQueue * only after CASing head field, so uses relaxed write. */ final void forgetNext() { - U.putObject(this, NEXT, this); + NEXT.set(this, this); } /** @@ -491,8 +493,8 @@ public class LinkedTransferQueue extends AbstractQueue * else we don't care). */ final void forgetContents() { - U.putObject(this, ITEM, this); - U.putObject(this, WAITER, null); + ITEM.set(this, this); + WAITER.set(this, null); } /** @@ -537,19 +539,16 @@ public class LinkedTransferQueue extends AbstractQueue private static final long serialVersionUID = -3375979862319811754L; - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long ITEM; - private static final long NEXT; - private static final long WAITER; + // VarHandle mechanics + private static final VarHandle ITEM; + private static final VarHandle NEXT; + private static final VarHandle WAITER; static { try { - ITEM = U.objectFieldOffset - (Node.class.getDeclaredField("item")); - NEXT = U.objectFieldOffset - (Node.class.getDeclaredField("next")); - WAITER = U.objectFieldOffset - (Node.class.getDeclaredField("waiter")); + MethodHandles.Lookup l = MethodHandles.lookup(); + ITEM = l.findVarHandle(Node.class, "item", Object.class); + NEXT = l.findVarHandle(Node.class, "next", Node.class); + WAITER = l.findVarHandle(Node.class, "waiter", Thread.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -567,15 +566,15 @@ public class LinkedTransferQueue extends AbstractQueue // CAS methods for fields private boolean casTail(Node cmp, Node val) { - return U.compareAndSwapObject(this, TAIL, cmp, val); + return TAIL.compareAndSet(this, cmp, val); } private boolean casHead(Node cmp, Node val) { - return U.compareAndSwapObject(this, HEAD, cmp, val); + return HEAD.compareAndSet(this, cmp, val); } private boolean casSweepVotes(int cmp, int val) { - return U.compareAndSwapInt(this, SWEEPVOTES, cmp, val); + return SWEEPVOTES.compareAndSet(this, cmp, val); } /* @@ -1562,20 +1561,19 @@ public class LinkedTransferQueue extends AbstractQueue } } - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long HEAD; - private static final long TAIL; - private static final long SWEEPVOTES; + // VarHandle mechanics + private static final VarHandle HEAD; + private static final VarHandle TAIL; + private static final VarHandle SWEEPVOTES; static { try { - HEAD = U.objectFieldOffset - (LinkedTransferQueue.class.getDeclaredField("head")); - TAIL = U.objectFieldOffset - (LinkedTransferQueue.class.getDeclaredField("tail")); - SWEEPVOTES = U.objectFieldOffset - (LinkedTransferQueue.class.getDeclaredField("sweepVotes")); + MethodHandles.Lookup l = MethodHandles.lookup(); + HEAD = l.findVarHandle(LinkedTransferQueue.class, "head", + Node.class); + TAIL = l.findVarHandle(LinkedTransferQueue.class, "tail", + Node.class); + SWEEPVOTES = l.findVarHandle(LinkedTransferQueue.class, "sweepVotes", + int.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java b/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java index 3fd30e7d3a8..c0e6fbadb01 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; @@ -221,7 +223,6 @@ import java.util.concurrent.locks.LockSupport; * phaser.arriveAndDeregister(); * }} * - * *

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 @@ -384,7 +385,7 @@ public class Phaser { int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK); if (unarrived <= 0) throw new IllegalStateException(badArrive(s)); - if (U.compareAndSwapLong(this, STATE, s, s-=adjust)) { + if (STATE.compareAndSet(this, s, s-=adjust)) { if (unarrived == 1) { long n = s & PARTIES_MASK; // base of next state int nextUnarrived = (int)n >>> PARTIES_SHIFT; @@ -397,12 +398,12 @@ public class Phaser { n |= nextUnarrived; int nextPhase = (phase + 1) & MAX_PHASE; n |= (long)nextPhase << PHASE_SHIFT; - U.compareAndSwapLong(this, STATE, s, n); + STATE.compareAndSet(this, s, n); releaseWaiters(phase); } else if (nextUnarrived == 0) { // propagate deregistration phase = parent.doArrive(ONE_DEREGISTER); - U.compareAndSwapLong(this, STATE, s, s | EMPTY); + STATE.compareAndSet(this, s, s | EMPTY); } else phase = parent.doArrive(ONE_ARRIVAL); @@ -437,13 +438,13 @@ public class Phaser { if (parent == null || reconcileState() == s) { if (unarrived == 0) // wait out advance root.internalAwaitAdvance(phase, null); - else if (U.compareAndSwapLong(this, STATE, s, s + adjust)) + else if (STATE.compareAndSet(this, s, s + adjust)) break; } } else if (parent == null) { // 1st root registration long next = ((long)phase << PHASE_SHIFT) | adjust; - if (U.compareAndSwapLong(this, STATE, s, next)) + if (STATE.compareAndSet(this, s, next)) break; } else { @@ -455,8 +456,8 @@ public class Phaser { // finish registration whenever parent registration // succeeded, even when racing with termination, // since these are part of the same "transaction". - while (!U.compareAndSwapLong - (this, STATE, s, + while (!STATE.weakCompareAndSetVolatile + (this, s, ((long)phase << PHASE_SHIFT) | adjust)) { s = state; phase = (int)(root.state >>> PHASE_SHIFT); @@ -487,8 +488,8 @@ public class Phaser { // CAS to root phase with current parties, tripping unarrived while ((phase = (int)(root.state >>> PHASE_SHIFT)) != (int)(s >>> PHASE_SHIFT) && - !U.compareAndSwapLong - (this, STATE, s, + !STATE.weakCompareAndSetVolatile + (this, s, s = (((long)phase << PHASE_SHIFT) | ((phase < 0) ? (s & COUNTS_MASK) : (((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY : @@ -677,7 +678,7 @@ public class Phaser { int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK); if (unarrived <= 0) throw new IllegalStateException(badArrive(s)); - if (U.compareAndSwapLong(this, STATE, s, s -= ONE_ARRIVAL)) { + if (STATE.compareAndSet(this, s, s -= ONE_ARRIVAL)) { if (unarrived > 1) return root.internalAwaitAdvance(phase, null); if (root != this) @@ -692,7 +693,7 @@ public class Phaser { n |= nextUnarrived; int nextPhase = (phase + 1) & MAX_PHASE; n |= (long)nextPhase << PHASE_SHIFT; - if (!U.compareAndSwapLong(this, STATE, s, n)) + if (!STATE.compareAndSet(this, s, n)) return (int)(state >>> PHASE_SHIFT); // terminated releaseWaiters(phase); return nextPhase; @@ -808,7 +809,7 @@ public class Phaser { final Phaser root = this.root; long s; while ((s = root.state) >= 0) { - if (U.compareAndSwapLong(root, STATE, s, s | TERMINATION_BIT)) { + if (STATE.compareAndSet(root, s, s | TERMINATION_BIT)) { // signal all threads releaseWaiters(0); // Waiters on evenQ releaseWaiters(1); // Waiters on oddQ @@ -1043,6 +1044,8 @@ public class Phaser { node = new QNode(this, phase, false, false, 0L); node.wasInterrupted = interrupted; } + else + Thread.onSpinWait(); } else if (node.isReleasable()) // done or aborted break; @@ -1131,14 +1134,12 @@ public class Phaser { } } - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long STATE; + // VarHandle mechanics + private static final VarHandle STATE; static { try { - STATE = U.objectFieldOffset - (Phaser.class.getDeclaredField("state")); + MethodHandles.Lookup l = MethodHandles.lookup(); + STATE = l.findVarHandle(Phaser.class, "state", long.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java b/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java index 652fd6958cf..36f5a47e3f9 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.AbstractQueue; import java.util.Arrays; import java.util.Collection; @@ -289,7 +291,7 @@ public class PriorityBlockingQueue extends AbstractQueue lock.unlock(); // must release and then re-acquire main lock Object[] newArray = null; if (allocationSpinLock == 0 && - U.compareAndSwapInt(this, ALLOCATIONSPINLOCK, 0, 1)) { + ALLOCATIONSPINLOCK.compareAndSet(this, 0, 1)) { try { int newCap = oldCap + ((oldCap < 64) ? (oldCap + 2) : // grow faster if small @@ -1009,13 +1011,14 @@ public class PriorityBlockingQueue extends AbstractQueue return new PBQSpliterator(this, null, 0, -1); } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long ALLOCATIONSPINLOCK; + // VarHandle mechanics + private static final VarHandle ALLOCATIONSPINLOCK; static { try { - ALLOCATIONSPINLOCK = U.objectFieldOffset - (PriorityBlockingQueue.class.getDeclaredField("allocationSpinLock")); + MethodHandles.Lookup l = MethodHandles.lookup(); + ALLOCATIONSPINLOCK = l.findVarHandle(PriorityBlockingQueue.class, + "allocationSpinLock", + int.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java b/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java index 069d0a6b851..e12d7afecc6 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java @@ -36,6 +36,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.AbstractQueue; import java.util.Collection; import java.util.Collections; @@ -247,7 +249,7 @@ public class SynchronousQueue extends AbstractQueue boolean casNext(SNode cmp, SNode val) { return cmp == next && - U.compareAndSwapObject(this, NEXT, cmp, val); + SNEXT.compareAndSet(this, cmp, val); } /** @@ -260,7 +262,7 @@ public class SynchronousQueue extends AbstractQueue */ boolean tryMatch(SNode s) { if (match == null && - U.compareAndSwapObject(this, MATCH, null, s)) { + SMATCH.compareAndSet(this, null, s)) { Thread w = waiter; if (w != null) { // waiters need at most one unpark waiter = null; @@ -275,24 +277,21 @@ public class SynchronousQueue extends AbstractQueue * Tries to cancel a wait by matching node to itself. */ void tryCancel() { - U.compareAndSwapObject(this, MATCH, null, this); + SMATCH.compareAndSet(this, null, this); } boolean isCancelled() { return match == this; } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long MATCH; - private static final long NEXT; - + // VarHandle mechanics + private static final VarHandle SMATCH; + private static final VarHandle SNEXT; static { try { - MATCH = U.objectFieldOffset - (SNode.class.getDeclaredField("match")); - NEXT = U.objectFieldOffset - (SNode.class.getDeclaredField("next")); + MethodHandles.Lookup l = MethodHandles.lookup(); + SMATCH = l.findVarHandle(SNode.class, "match", SNode.class); + SNEXT = l.findVarHandle(SNode.class, "next", SNode.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -304,7 +303,7 @@ public class SynchronousQueue extends AbstractQueue boolean casHead(SNode h, SNode nh) { return h == head && - U.compareAndSwapObject(this, HEAD, h, nh); + SHEAD.compareAndSet(this, h, nh); } /** @@ -451,8 +450,10 @@ public class SynchronousQueue extends AbstractQueue continue; } } - if (spins > 0) + if (spins > 0) { + Thread.onSpinWait(); spins = shouldSpin(s) ? (spins - 1) : 0; + } else if (s.waiter == null) s.waiter = w; // establish waiter so can park next iter else if (!timed) @@ -508,13 +509,12 @@ public class SynchronousQueue extends AbstractQueue } } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long HEAD; + // VarHandle mechanics + private static final VarHandle SHEAD; static { try { - HEAD = U.objectFieldOffset - (TransferStack.class.getDeclaredField("head")); + MethodHandles.Lookup l = MethodHandles.lookup(); + SHEAD = l.findVarHandle(TransferStack.class, "head", SNode.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -546,19 +546,19 @@ public class SynchronousQueue extends AbstractQueue boolean casNext(QNode cmp, QNode val) { return next == cmp && - U.compareAndSwapObject(this, NEXT, cmp, val); + QNEXT.compareAndSet(this, cmp, val); } boolean casItem(Object cmp, Object val) { return item == cmp && - U.compareAndSwapObject(this, ITEM, cmp, val); + QITEM.compareAndSet(this, cmp, val); } /** * Tries to cancel by CAS'ing ref to this as item. */ void tryCancel(Object cmp) { - U.compareAndSwapObject(this, ITEM, cmp, this); + QITEM.compareAndSet(this, cmp, this); } boolean isCancelled() { @@ -574,17 +574,14 @@ public class SynchronousQueue extends AbstractQueue return next == this; } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long ITEM; - private static final long NEXT; - + // VarHandle mechanics + private static final VarHandle QITEM; + private static final VarHandle QNEXT; static { try { - ITEM = U.objectFieldOffset - (QNode.class.getDeclaredField("item")); - NEXT = U.objectFieldOffset - (QNode.class.getDeclaredField("next")); + MethodHandles.Lookup l = MethodHandles.lookup(); + QITEM = l.findVarHandle(QNode.class, "item", Object.class); + QNEXT = l.findVarHandle(QNode.class, "next", QNode.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -614,7 +611,7 @@ public class SynchronousQueue extends AbstractQueue */ void advanceHead(QNode h, QNode nh) { if (h == head && - U.compareAndSwapObject(this, HEAD, h, nh)) + QHEAD.compareAndSet(this, h, nh)) h.next = h; // forget old next } @@ -623,7 +620,7 @@ public class SynchronousQueue extends AbstractQueue */ void advanceTail(QNode t, QNode nt) { if (tail == t) - U.compareAndSwapObject(this, TAIL, t, nt); + QTAIL.compareAndSet(this, t, nt); } /** @@ -631,7 +628,7 @@ public class SynchronousQueue extends AbstractQueue */ boolean casCleanMe(QNode cmp, QNode val) { return cleanMe == cmp && - U.compareAndSwapObject(this, CLEANME, cmp, val); + QCLEANME.compareAndSet(this, cmp, val); } /** @@ -752,8 +749,10 @@ public class SynchronousQueue extends AbstractQueue continue; } } - if (spins > 0) + if (spins > 0) { --spins; + Thread.onSpinWait(); + } else if (s.waiter == null) s.waiter = w; else if (!timed) @@ -817,18 +816,19 @@ public class SynchronousQueue extends AbstractQueue } } - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long HEAD; - private static final long TAIL; - private static final long CLEANME; + // VarHandle mechanics + private static final VarHandle QHEAD; + private static final VarHandle QTAIL; + private static final VarHandle QCLEANME; static { try { - HEAD = U.objectFieldOffset - (TransferQueue.class.getDeclaredField("head")); - TAIL = U.objectFieldOffset - (TransferQueue.class.getDeclaredField("tail")); - CLEANME = U.objectFieldOffset - (TransferQueue.class.getDeclaredField("cleanMe")); + MethodHandles.Lookup l = MethodHandles.lookup(); + QHEAD = l.findVarHandle(TransferQueue.class, "head", + QNode.class); + QTAIL = l.findVarHandle(TransferQueue.class, "tail", + QNode.class); + QCLEANME = l.findVarHandle(TransferQueue.class, "cleanMe", + QNode.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java index 5241392ed92..d32624aaab7 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java @@ -35,27 +35,26 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; + /** * A {@code boolean} value that may be updated atomically. See the - * {@link java.util.concurrent.atomic} package specification for - * description of the properties of atomic variables. An - * {@code AtomicBoolean} is used in applications such as atomically - * updated flags, and cannot be used as a replacement for a - * {@link java.lang.Boolean}. + * {@link VarHandle} specification for descriptions of the properties + * of atomic accesses. An {@code AtomicBoolean} is used in + * applications such as atomically updated flags, and cannot be used + * as a replacement for a {@link java.lang.Boolean}. * * @since 1.5 * @author Doug Lea */ public class AtomicBoolean implements java.io.Serializable { private static final long serialVersionUID = 4654671469794556979L; - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long VALUE; - + private static final VarHandle VALUE; static { try { - VALUE = U.objectFieldOffset - (AtomicBoolean.class.getDeclaredField("value")); + MethodHandles.Lookup l = MethodHandles.lookup(); + VALUE = l.findVarHandle(AtomicBoolean.class, "value", int.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -79,7 +78,8 @@ public class AtomicBoolean implements java.io.Serializable { } /** - * Returns the current value. + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getVolatile}. * * @return the current value */ @@ -88,40 +88,39 @@ public class AtomicBoolean implements java.io.Serializable { } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. + * Atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#compareAndSet}. * - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ - public final boolean compareAndSet(boolean expect, boolean update) { - return U.compareAndSwapInt(this, VALUE, - (expect ? 1 : 0), - (update ? 1 : 0)); + public final boolean compareAndSet(boolean expectedValue, boolean newValue) { + return VALUE.compareAndSet(this, + (expectedValue ? 1 : 0), + (newValue ? 1 : 0)); } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#weakCompareAndSet}. * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. - * - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful */ - public boolean weakCompareAndSet(boolean expect, boolean update) { - return U.compareAndSwapInt(this, VALUE, - (expect ? 1 : 0), - (update ? 1 : 0)); + public boolean weakCompareAndSet(boolean expectedValue, boolean newValue) { + return VALUE.weakCompareAndSet(this, + (expectedValue ? 1 : 0), + (newValue ? 1 : 0)); } /** - * Unconditionally sets to the given value. + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setVolatile}. * * @param newValue the new value */ @@ -130,17 +129,19 @@ public class AtomicBoolean implements java.io.Serializable { } /** - * Eventually sets to the given value. + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. * * @param newValue the new value * @since 1.6 */ public final void lazySet(boolean newValue) { - U.putIntRelease(this, VALUE, (newValue ? 1 : 0)); + VALUE.setRelease(this, (newValue ? 1 : 0)); } /** - * Atomically sets to the given value and returns the previous value. + * Atomically sets the value to {@code newValue} and returns the old value, + * with memory effects as specified by {@link VarHandle#getAndSet}. * * @param newValue the new value * @return the previous value @@ -161,4 +162,178 @@ public class AtomicBoolean implements java.io.Serializable { return Boolean.toString(get()); } + // jdk9 + + /** + * Returns the current value, with memory semantics of reading as + * if the variable was declared non-{@code volatile}. + * + * @return the value + * @since 9 + */ + public final boolean getPlain() { + return (int)VALUE.get(this) != 0; + } + + /** + * Sets the value to {@code newValue}, with memory semantics + * of setting as if the variable was declared non-{@code volatile} + * and non-{@code final}. + * + * @param newValue the new value + * @since 9 + */ + public final void setPlain(boolean newValue) { + VALUE.set(this, newValue ? 1 : 0); + } + + /** + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getOpaque}. + * + * @return the value + * @since 9 + */ + public final boolean getOpaque() { + return (int)VALUE.getOpaque(this) != 0; + } + + /** + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setOpaque}. + * + * @param newValue the new value + * @since 9 + */ + public final void setOpaque(boolean newValue) { + VALUE.setOpaque(this, newValue ? 1 : 0); + } + + /** + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getAcquire}. + * + * @return the value + * @since 9 + */ + public final boolean getAcquire() { + return (int)VALUE.getAcquire(this) != 0; + } + + /** + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. + * + * @param newValue the new value + * @since 9 + */ + public final void setRelease(boolean newValue) { + VALUE.setRelease(this, newValue ? 1 : 0); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchange}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final boolean compareAndExchange(boolean expectedValue, boolean newValue) { + return (int)VALUE.compareAndExchange(this, + (expectedValue ? 1 : 0), + (newValue ? 1 : 0)) != 0; + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeAcquire}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final boolean compareAndExchangeAcquire(boolean expectedValue, boolean newValue) { + return (int)VALUE.compareAndExchangeAcquire(this, + (expectedValue ? 1 : 0), + (newValue ? 1 : 0)) != 0; + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeRelease}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final boolean compareAndExchangeRelease(boolean expectedValue, boolean newValue) { + return (int)VALUE.compareAndExchangeRelease(this, + (expectedValue ? 1 : 0), + (newValue ? 1 : 0)) != 0; + } + + /** + * Possibly atomically sets the value to {@code newValue} if the current + * value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetVolatile}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetVolatile(boolean expectedValue, boolean newValue) { + return VALUE.weakCompareAndSetVolatile(this, + (expectedValue ? 1 : 0), + (newValue ? 1 : 0)); + } + + /** + * Possibly atomically sets the value to {@code newValue} if the current + * value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetAcquire}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetAcquire(boolean expectedValue, boolean newValue) { + return VALUE.weakCompareAndSetAcquire(this, + (expectedValue ? 1 : 0), + (newValue ? 1 : 0)); + } + + /** + * Possibly atomically sets the value to {@code newValue} if the current + * value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetRelease}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetRelease(boolean expectedValue, boolean newValue) { + return VALUE.weakCompareAndSetRelease(this, + (expectedValue ? 1 : 0), + (newValue ? 1 : 0)); + } + } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java index 920b45bd5ab..a6221a86a80 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java @@ -35,32 +35,30 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.function.IntBinaryOperator; import java.util.function.IntUnaryOperator; /** * An {@code int} value that may be updated atomically. See the - * {@link java.util.concurrent.atomic} package specification for - * description of the properties of atomic variables. An - * {@code AtomicInteger} is used in applications such as atomically - * incremented counters, and cannot be used as a replacement for an - * {@link java.lang.Integer}. However, this class does extend - * {@code Number} to allow uniform access by tools and utilities that - * deal with numerically-based classes. + * {@link VarHandle} specification for descriptions of the properties + * of atomic accesses. An {@code AtomicInteger} is used in + * applications such as atomically incremented counters, and cannot be + * used as a replacement for an {@link java.lang.Integer}. However, + * this class does extend {@code Number} to allow uniform access by + * tools and utilities that deal with numerically-based classes. * * @since 1.5 * @author Doug Lea */ public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long VALUE; - + private static final VarHandle VALUE; static { try { - VALUE = U.objectFieldOffset - (AtomicInteger.class.getDeclaredField("value")); + MethodHandles.Lookup l = MethodHandles.lookup(); + VALUE = l.findVarHandle(AtomicInteger.class, "value", int.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -84,7 +82,8 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * Gets the current value. + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getVolatile}. * * @return the current value */ @@ -93,7 +92,8 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * Sets to the given value. + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setVolatile}. * * @param newValue the new value */ @@ -102,108 +102,122 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * Eventually sets to the given value. + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. * * @param newValue the new value * @since 1.6 */ public final void lazySet(int newValue) { - U.putIntRelease(this, VALUE, newValue); + VALUE.setRelease(this, newValue); } /** - * Atomically sets to the given value and returns the old value. + * Atomically sets the value to {@code newValue} and returns the old value, + * with memory effects as specified by {@link VarHandle#getAndSet}. * * @param newValue the new value * @return the previous value */ public final int getAndSet(int newValue) { - return U.getAndSetInt(this, VALUE, newValue); + return (int)VALUE.getAndSet(this, newValue); } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. + * Atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#compareAndSet}. * - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ - public final boolean compareAndSet(int expect, int update) { - return U.compareAndSwapInt(this, VALUE, expect, update); + public final boolean compareAndSet(int expectedValue, int newValue) { + return VALUE.compareAndSet(this, expectedValue, newValue); } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#weakCompareAndSet}. * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. - * - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful */ - public final boolean weakCompareAndSet(int expect, int update) { - return U.compareAndSwapInt(this, VALUE, expect, update); + public final boolean weakCompareAndSet(int expectedValue, int newValue) { + return VALUE.weakCompareAndSet(this, expectedValue, newValue); } /** - * Atomically increments by one the current value. + * Atomically increments the current value, + * with memory effects as specified by {@link VarHandle#getAndAdd}. + * + *

Equivalent to {@code getAndAdd(1)}. * * @return the previous value */ public final int getAndIncrement() { - return U.getAndAddInt(this, VALUE, 1); + return (int)VALUE.getAndAdd(this, 1); } /** - * Atomically decrements by one the current value. + * Atomically decrements the current value, + * with memory effects as specified by {@link VarHandle#getAndAdd}. + * + *

Equivalent to {@code getAndAdd(-1)}. * * @return the previous value */ public final int getAndDecrement() { - return U.getAndAddInt(this, VALUE, -1); + return (int)VALUE.getAndAdd(this, -1); } /** - * Atomically adds the given value to the current value. + * Atomically adds the given value to the current value, + * with memory effects as specified by {@link VarHandle#getAndAdd}. * * @param delta the value to add * @return the previous value */ public final int getAndAdd(int delta) { - return U.getAndAddInt(this, VALUE, delta); + return (int)VALUE.getAndAdd(this, delta); } /** - * Atomically increments by one the current value. + * Atomically increments the current value, + * with memory effects as specified by {@link VarHandle#addAndGet}. + * + *

Equivalent to {@code addAndGet(1)}. * * @return the updated value */ public final int incrementAndGet() { - return U.getAndAddInt(this, VALUE, 1) + 1; + return (int)VALUE.addAndGet(this, 1); } /** - * Atomically decrements by one the current value. + * Atomically decrements the current value, + * with memory effects as specified by {@link VarHandle#addAndGet}. + * + *

Equivalent to {@code addAndGet(-1)}. * * @return the updated value */ public final int decrementAndGet() { - return U.getAndAddInt(this, VALUE, -1) - 1; + return (int)VALUE.addAndGet(this, -1); } /** - * Atomically adds the given value to the current value. + * Atomically adds the given value to the current value, + * with memory effects as specified by {@link VarHandle#addAndGet}. * * @param delta the value to add * @return the updated value */ public final int addAndGet(int delta) { - return U.getAndAddInt(this, VALUE, delta) + delta; + return (int)VALUE.addAndGet(this, delta); } /** @@ -217,12 +231,14 @@ public class AtomicInteger extends Number implements java.io.Serializable { * @since 1.8 */ public final int getAndUpdate(IntUnaryOperator updateFunction) { - int prev, next; - do { - prev = get(); - next = updateFunction.applyAsInt(prev); - } while (!compareAndSet(prev, next)); - return prev; + int prev = get(), next = 0; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.applyAsInt(prev); + if (weakCompareAndSetVolatile(prev, next)) + return prev; + haveNext = (prev == (prev = get())); + } } /** @@ -236,12 +252,14 @@ public class AtomicInteger extends Number implements java.io.Serializable { * @since 1.8 */ public final int updateAndGet(IntUnaryOperator updateFunction) { - int prev, next; - do { - prev = get(); - next = updateFunction.applyAsInt(prev); - } while (!compareAndSet(prev, next)); - return next; + int prev = get(), next = 0; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.applyAsInt(prev); + if (weakCompareAndSetVolatile(prev, next)) + return next; + haveNext = (prev == (prev = get())); + } } /** @@ -260,12 +278,14 @@ public class AtomicInteger extends Number implements java.io.Serializable { */ public final int getAndAccumulate(int x, IntBinaryOperator accumulatorFunction) { - int prev, next; - do { - prev = get(); - next = accumulatorFunction.applyAsInt(prev, x); - } while (!compareAndSet(prev, next)); - return prev; + int prev = get(), next = 0; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.applyAsInt(prev, x); + if (weakCompareAndSetVolatile(prev, next)) + return prev; + haveNext = (prev == (prev = get())); + } } /** @@ -284,12 +304,14 @@ public class AtomicInteger extends Number implements java.io.Serializable { */ public final int accumulateAndGet(int x, IntBinaryOperator accumulatorFunction) { - int prev, next; - do { - prev = get(); - next = accumulatorFunction.applyAsInt(prev, x); - } while (!compareAndSet(prev, next)); - return next; + int prev = get(), next = 0; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.applyAsInt(prev, x); + if (weakCompareAndSetVolatile(prev, next)) + return next; + haveNext = (prev == (prev = get())); + } } /** @@ -301,7 +323,10 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * Returns the value of this {@code AtomicInteger} as an {@code int}. + * Returns the current value of this {@code AtomicInteger} as an + * {@code int}, + * with memory effects as specified by {@link VarHandle#getVolatile}. + * * Equivalent to {@link #get()}. */ public int intValue() { @@ -309,8 +334,9 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * Returns the value of this {@code AtomicInteger} as a {@code long} - * after a widening primitive conversion. + * Returns the current value of this {@code AtomicInteger} as a + * {@code long} after a widening primitive conversion, + * with memory effects as specified by {@link VarHandle#getVolatile}. * @jls 5.1.2 Widening Primitive Conversions */ public long longValue() { @@ -318,8 +344,9 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * Returns the value of this {@code AtomicInteger} as a {@code float} - * after a widening primitive conversion. + * Returns the current value of this {@code AtomicInteger} as a + * {@code float} after a widening primitive conversion, + * with memory effects as specified by {@link VarHandle#getVolatile}. * @jls 5.1.2 Widening Primitive Conversions */ public float floatValue() { @@ -327,12 +354,175 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * Returns the value of this {@code AtomicInteger} as a {@code double} - * after a widening primitive conversion. + * Returns the current value of this {@code AtomicInteger} as a + * {@code double} after a widening primitive conversion, + * with memory effects as specified by {@link VarHandle#getVolatile}. * @jls 5.1.2 Widening Primitive Conversions */ public double doubleValue() { return (double)get(); } + // jdk9 + + /** + * Returns the current value, with memory semantics of reading as + * if the variable was declared non-{@code volatile}. + * + * @return the value + * @since 9 + */ + public final int getPlain() { + return (int)VALUE.get(this); + } + + /** + * Sets the value to {@code newValue}, with memory semantics + * of setting as if the variable was declared non-{@code volatile} + * and non-{@code final}. + * + * @param newValue the new value + * @since 9 + */ + public final void setPlain(int newValue) { + VALUE.set(this, newValue); + } + + /** + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getOpaque}. + * + * @return the value + * @since 9 + */ + public final int getOpaque() { + return (int)VALUE.getOpaque(this); + } + + /** + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setOpaque}. + * + * @param newValue the new value + * @since 9 + */ + public final void setOpaque(int newValue) { + VALUE.setOpaque(this, newValue); + } + + /** + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getAcquire}. + * + * @return the value + * @since 9 + */ + public final int getAcquire() { + return (int)VALUE.getAcquire(this); + } + + /** + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. + * + * @param newValue the new value + * @since 9 + */ + public final void setRelease(int newValue) { + VALUE.setRelease(this, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchange}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final int compareAndExchange(int expectedValue, int newValue) { + return (int)VALUE.compareAndExchange(this, expectedValue, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeAcquire}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final int compareAndExchangeAcquire(int expectedValue, int newValue) { + return (int)VALUE.compareAndExchangeAcquire(this, expectedValue, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeRelease}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final int compareAndExchangeRelease(int expectedValue, int newValue) { + return (int)VALUE.compareAndExchangeRelease(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} if + * the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetVolatile}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetVolatile(int expectedValue, int newValue) { + return VALUE.weakCompareAndSetVolatile(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} if + * the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetAcquire}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetAcquire(int expectedValue, int newValue) { + return VALUE.weakCompareAndSetAcquire(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} if + * the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetRelease}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetRelease(int expectedValue, int newValue) { + return VALUE.weakCompareAndSetRelease(this, expectedValue, newValue); + } + } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java index 001a35008d9..301aa4a5844 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java @@ -35,44 +35,24 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.function.IntBinaryOperator; import java.util.function.IntUnaryOperator; /** * An {@code int} array in which elements may be updated atomically. - * See the {@link java.util.concurrent.atomic} package - * specification for description of the properties of atomic - * variables. + * See the {@link VarHandle} specification for descriptions of the + * properties of atomic accesses. * @since 1.5 * @author Doug Lea */ public class AtomicIntegerArray implements java.io.Serializable { private static final long serialVersionUID = 2862133569453604235L; - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final int ABASE; - private static final int ASHIFT; + private static final VarHandle AA + = MethodHandles.arrayElementVarHandle(int[].class); private final int[] array; - static { - ABASE = U.arrayBaseOffset(int[].class); - int scale = U.arrayIndexScale(int[].class); - if ((scale & (scale - 1)) != 0) - throw new Error("array index scale not a power of two"); - ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); - } - - private long checkedByteOffset(int i) { - if (i < 0 || i >= array.length) - throw new IndexOutOfBoundsException("index " + i); - - return byteOffset(i); - } - - private static long byteOffset(int i) { - return ((long) i << ASHIFT) + ABASE; - } - /** * Creates a new AtomicIntegerArray of the given length, with all * elements initially zero. @@ -105,147 +85,155 @@ public class AtomicIntegerArray implements java.io.Serializable { } /** - * Gets the current value at position {@code i}. + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getVolatile}. * * @param i the index * @return the current value */ public final int get(int i) { - return getRaw(checkedByteOffset(i)); - } - - private int getRaw(long offset) { - return U.getIntVolatile(array, offset); + return (int)AA.getVolatile(array, i); } /** - * Sets the element at position {@code i} to the given value. + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setVolatile}. * * @param i the index * @param newValue the new value */ public final void set(int i, int newValue) { - U.putIntVolatile(array, checkedByteOffset(i), newValue); + AA.setVolatile(array, i, newValue); } /** - * Eventually sets the element at position {@code i} to the given value. + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. * * @param i the index * @param newValue the new value * @since 1.6 */ public final void lazySet(int i, int newValue) { - U.putIntRelease(array, checkedByteOffset(i), newValue); + AA.setRelease(array, i, newValue); } /** - * Atomically sets the element at position {@code i} to the given - * value and returns the old value. + * Atomically sets the element at index {@code i} to {@code + * newValue} and returns the old value, + * with memory effects as specified by {@link VarHandle#getAndSet}. * * @param i the index * @param newValue the new value * @return the previous value */ public final int getAndSet(int i, int newValue) { - return U.getAndSetInt(array, checkedByteOffset(i), newValue); + return (int)AA.getAndSet(array, i, newValue); } /** - * Atomically sets the element at position {@code i} to the given - * updated value if the current value {@code ==} the expected value. + * Atomically sets the element at index {@code i} to {@code + * newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#compareAndSet}. * * @param i the index - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ - public final boolean compareAndSet(int i, int expect, int update) { - return compareAndSetRaw(checkedByteOffset(i), expect, update); - } - - private boolean compareAndSetRaw(long offset, int expect, int update) { - return U.compareAndSwapInt(array, offset, expect, update); + public final boolean compareAndSet(int i, int expectedValue, int newValue) { + return AA.compareAndSet(array, i, expectedValue, newValue); } /** - * 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 appropriate alternative to {@code compareAndSet}. + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#weakCompareAndSet}. * * @param i the index - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful */ - public final boolean weakCompareAndSet(int i, int expect, int update) { - return compareAndSet(i, expect, update); + public final boolean weakCompareAndSet(int i, int expectedValue, int newValue) { + return AA.weakCompareAndSet(array, i, expectedValue, newValue); } /** - * Atomically increments by one the element at index {@code i}. + * Atomically increments the value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAndAdd}. + * + *

Equivalent to {@code getAndAdd(i, 1)}. * * @param i the index * @return the previous value */ public final int getAndIncrement(int i) { - return getAndAdd(i, 1); + return (int)AA.getAndAdd(array, i, 1); } /** - * Atomically decrements by one the element at index {@code i}. + * Atomically decrements the value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAndAdd}. + * + *

Equivalent to {@code getAndAdd(i, -1)}. * * @param i the index * @return the previous value */ public final int getAndDecrement(int i) { - return getAndAdd(i, -1); + return (int)AA.getAndAdd(array, i, -1); } /** - * Atomically adds the given value to the element at index {@code i}. + * Atomically adds the given value to the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAndAdd}. * * @param i the index * @param delta the value to add * @return the previous value */ public final int getAndAdd(int i, int delta) { - return U.getAndAddInt(array, checkedByteOffset(i), delta); + return (int)AA.getAndAdd(array, i, delta); } /** - * Atomically increments by one the element at index {@code i}. + * Atomically increments the value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#addAndGet}. + * + *

Equivalent to {@code addAndGet(i, 1)}. * * @param i the index * @return the updated value */ public final int incrementAndGet(int i) { - return getAndAdd(i, 1) + 1; + return (int)AA.addAndGet(array, i, 1); } /** - * Atomically decrements by one the element at index {@code i}. + * Atomically decrements the value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#addAndGet}. + * + *

Equivalent to {@code addAndGet(i, -1)}. * * @param i the index * @return the updated value */ public final int decrementAndGet(int i) { - return getAndAdd(i, -1) - 1; + return (int)AA.addAndGet(array, i, -1); } /** - * Atomically adds the given value to the element at index {@code i}. + * Atomically adds the given value to the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#addAndGet}. * * @param i the index * @param delta the value to add * @return the updated value */ public final int addAndGet(int i, int delta) { - return getAndAdd(i, delta) + delta; + return (int)AA.addAndGet(array, i, delta); } /** @@ -260,13 +248,14 @@ public class AtomicIntegerArray implements java.io.Serializable { * @since 1.8 */ public final int getAndUpdate(int i, IntUnaryOperator updateFunction) { - long offset = checkedByteOffset(i); - int prev, next; - do { - prev = getRaw(offset); - next = updateFunction.applyAsInt(prev); - } while (!compareAndSetRaw(offset, prev, next)); - return prev; + int prev = get(i), next = 0; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.applyAsInt(prev); + if (weakCompareAndSetVolatile(i, prev, next)) + return prev; + haveNext = (prev == (prev = get(i))); + } } /** @@ -281,23 +270,25 @@ public class AtomicIntegerArray implements java.io.Serializable { * @since 1.8 */ public final int updateAndGet(int i, IntUnaryOperator updateFunction) { - long offset = checkedByteOffset(i); - int prev, next; - do { - prev = getRaw(offset); - next = updateFunction.applyAsInt(prev); - } while (!compareAndSetRaw(offset, prev, next)); - return next; + int prev = get(i), next = 0; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.applyAsInt(prev); + if (weakCompareAndSetVolatile(i, prev, next)) + return next; + haveNext = (prev == (prev = get(i))); + } } /** * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the previous value. The function should - * be side-effect-free, since it may be re-applied when attempted + * results of applying the given function to the current and given + * values, returning the previous value. The function should be + * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. + * applied with the current value of the element at index {@code i} + * as its first argument, and the given update as the second + * argument. * * @param i the index * @param x the update value @@ -307,23 +298,25 @@ public class AtomicIntegerArray implements java.io.Serializable { */ public final int getAndAccumulate(int i, int x, IntBinaryOperator accumulatorFunction) { - long offset = checkedByteOffset(i); - int prev, next; - do { - prev = getRaw(offset); - next = accumulatorFunction.applyAsInt(prev, x); - } while (!compareAndSetRaw(offset, prev, next)); - return prev; + int prev = get(i), next = 0; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.applyAsInt(prev, x); + if (weakCompareAndSetVolatile(i, prev, next)) + return prev; + haveNext = (prev == (prev = get(i))); + } } /** * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the updated value. The function should - * be side-effect-free, since it may be re-applied when attempted + * results of applying the given function to the current and given + * values, returning the updated value. The function should be + * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. + * applied with the current value of the element at index {@code i} + * as its first argument, and the given update as the second + * argument. * * @param i the index * @param x the update value @@ -333,13 +326,14 @@ public class AtomicIntegerArray implements java.io.Serializable { */ public final int accumulateAndGet(int i, int x, IntBinaryOperator accumulatorFunction) { - long offset = checkedByteOffset(i); - int prev, next; - do { - prev = getRaw(offset); - next = accumulatorFunction.applyAsInt(prev, x); - } while (!compareAndSetRaw(offset, prev, next)); - return next; + int prev = get(i), next = 0; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.applyAsInt(prev, x); + if (weakCompareAndSetVolatile(i, prev, next)) + return next; + haveNext = (prev == (prev = get(i))); + } } /** @@ -354,11 +348,190 @@ public class AtomicIntegerArray implements java.io.Serializable { StringBuilder b = new StringBuilder(); b.append('['); for (int i = 0; ; i++) { - b.append(getRaw(byteOffset(i))); + b.append(get(i)); if (i == iMax) return b.append(']').toString(); b.append(',').append(' '); } } + // jdk9 + + /** + * Returns the current value of the element at index {@code i}, + * with memory semantics of reading as if the variable was declared + * non-{@code volatile}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final int getPlain(int i) { + return (int)AA.get(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory semantics of setting as if the variable was + * declared non-{@code volatile} and non-{@code final}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setPlain(int i, int newValue) { + AA.set(array, i, newValue); + } + + /** + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getOpaque}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final int getOpaque(int i) { + return (int)AA.getOpaque(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setOpaque}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setOpaque(int i, int newValue) { + AA.setOpaque(array, i, newValue); + } + + /** + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAcquire}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final int getAcquire(int i) { + return (int)AA.getAcquire(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setRelease(int i, int newValue) { + AA.setRelease(array, i, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchange}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final int compareAndExchange(int i, int expectedValue, int newValue) { + return (int)AA.compareAndExchange(array, i, expectedValue, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeAcquire}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final int compareAndExchangeAcquire(int i, int expectedValue, int newValue) { + return (int)AA.compareAndExchangeAcquire(array, i, expectedValue, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeRelease}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final int compareAndExchangeRelease(int i, int expectedValue, int newValue) { + return (int)AA.compareAndExchangeRelease(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetVolatile}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetVolatile(int i, int expectedValue, int newValue) { + return AA.weakCompareAndSetVolatile(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetAcquire}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetAcquire(int i, int expectedValue, int newValue) { + return AA.weakCompareAndSetAcquire(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetRelease}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetRelease(int i, int expectedValue, int newValue) { + return AA.weakCompareAndSetRelease(array, i, expectedValue, newValue); + } + + } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java index 10ee364b5b9..c86f41ddee7 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java @@ -42,6 +42,7 @@ import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.function.IntBinaryOperator; import java.util.function.IntUnaryOperator; +import jdk.internal.misc.Unsafe; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; @@ -150,8 +151,8 @@ public abstract class AtomicIntegerFieldUpdater { public abstract void lazySet(T obj, int newValue); /** - * Gets the current value held in the field of the given object managed - * by this updater. + * Returns the current value held in the field of the given object + * managed by this updater. * * @param obj An object whose field to get * @return the current value @@ -367,7 +368,7 @@ public abstract class AtomicIntegerFieldUpdater { */ private static final class AtomicIntegerFieldUpdaterImpl extends AtomicIntegerFieldUpdater { - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); + private static final Unsafe U = Unsafe.getUnsafe(); private final long offset; /** * if field is protected, the subclass constructing updater, else diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java index 85ff9030da3..2c79beaadae 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java @@ -35,31 +35,30 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.function.LongBinaryOperator; import java.util.function.LongUnaryOperator; /** * A {@code long} value that may be updated atomically. See the - * {@link java.util.concurrent.atomic} package specification for - * description of the properties of atomic variables. An - * {@code AtomicLong} is used in applications such as atomically - * incremented sequence numbers, and cannot be used as a replacement - * for a {@link java.lang.Long}. However, this class does extend - * {@code Number} to allow uniform access by tools and utilities that - * deal with numerically-based classes. + * {@link VarHandle} specification for descriptions of the properties + * of atomic accesses. An {@code AtomicLong} is used in applications + * such as atomically incremented sequence numbers, and cannot be used + * as a replacement for a {@link java.lang.Long}. However, this class + * does extend {@code Number} to allow uniform access by tools and + * utilities that deal with numerically-based classes. * * @since 1.5 * @author Doug Lea */ public class AtomicLong extends Number implements java.io.Serializable { private static final long serialVersionUID = 1927816293512124184L; - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long VALUE; + private static final VarHandle VALUE; /** * Records whether the underlying JVM supports lockless - * compareAndSwap for longs. While the Unsafe.compareAndSwapLong + * compareAndSwap for longs. While the intrinsic compareAndSwapLong * method works in either case, some constructions should be * handled at Java level to avoid locking user-visible locks. */ @@ -73,8 +72,8 @@ public class AtomicLong extends Number implements java.io.Serializable { static { try { - VALUE = U.objectFieldOffset - (AtomicLong.class.getDeclaredField("value")); + MethodHandles.Lookup l = MethodHandles.lookup(); + VALUE = l.findVarHandle(AtomicLong.class, "value", long.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -98,7 +97,8 @@ public class AtomicLong extends Number implements java.io.Serializable { } /** - * Gets the current value. + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getVolatile}. * * @return the current value */ @@ -107,119 +107,132 @@ public class AtomicLong extends Number implements java.io.Serializable { } /** - * Sets to the given value. + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setVolatile}. * * @param newValue the new value */ public final void set(long newValue) { - // Use putLongVolatile instead of ordinary volatile store when - // using compareAndSwapLong, for sake of some 32bit systems. - U.putLongVolatile(this, VALUE, newValue); + VALUE.setVolatile(this, newValue); } /** - * Eventually sets to the given value. + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. * * @param newValue the new value * @since 1.6 */ public final void lazySet(long newValue) { - U.putLongRelease(this, VALUE, newValue); + VALUE.setRelease(this, newValue); } /** - * Atomically sets to the given value and returns the old value. + * Atomically sets the value to {@code newValue} and returns the old value, + * with memory effects as specified by {@link VarHandle#getAndSet}. * * @param newValue the new value * @return the previous value */ public final long getAndSet(long newValue) { - return U.getAndSetLong(this, VALUE, newValue); + return (long)VALUE.getAndSet(this, newValue); } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. + * Atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#compareAndSet}. * - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ - public final boolean compareAndSet(long expect, long update) { - return U.compareAndSwapLong(this, VALUE, expect, update); + public final boolean compareAndSet(long expectedValue, long newValue) { + return VALUE.compareAndSet(this, expectedValue, newValue); } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#weakCompareAndSet}. * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. - * - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful */ - public final boolean weakCompareAndSet(long expect, long update) { - return U.compareAndSwapLong(this, VALUE, expect, update); + public final boolean weakCompareAndSet(long expectedValue, long newValue) { + return VALUE.weakCompareAndSet(this, expectedValue, newValue); } /** - * Atomically increments by one the current value. + * Atomically increments the current value, + * with memory effects as specified by {@link VarHandle#getAndAdd}. + * + *

Equivalent to {@code getAndAdd(1)}. * * @return the previous value */ public final long getAndIncrement() { - return U.getAndAddLong(this, VALUE, 1L); + return (long)VALUE.getAndAdd(this, 1L); } /** - * Atomically decrements by one the current value. + * Atomically decrements the current value, + * with memory effects as specified by {@link VarHandle#getAndAdd}. + * + *

Equivalent to {@code getAndAdd(-1)}. * * @return the previous value */ public final long getAndDecrement() { - return U.getAndAddLong(this, VALUE, -1L); + return (long)VALUE.getAndAdd(this, -1L); } /** - * Atomically adds the given value to the current value. + * Atomically adds the given value to the current value, + * with memory effects as specified by {@link VarHandle#getAndAdd}. * * @param delta the value to add * @return the previous value */ public final long getAndAdd(long delta) { - return U.getAndAddLong(this, VALUE, delta); + return (long)VALUE.getAndAdd(this, delta); } /** - * Atomically increments by one the current value. + * Atomically increments the current value, + * with memory effects as specified by {@link VarHandle#addAndGet}. + * + *

Equivalent to {@code addAndGet(1)}. * * @return the updated value */ public final long incrementAndGet() { - return U.getAndAddLong(this, VALUE, 1L) + 1L; + return (long)VALUE.addAndGet(this, 1L); } /** - * Atomically decrements by one the current value. + * Atomically decrements the current value, + * with memory effects as specified by {@link VarHandle#addAndGet}. + * + *

Equivalent to {@code addAndGet(-1)}. * * @return the updated value */ public final long decrementAndGet() { - return U.getAndAddLong(this, VALUE, -1L) - 1L; + return (long)VALUE.addAndGet(this, -1L); } /** - * Atomically adds the given value to the current value. + * Atomically adds the given value to the current value, + * with memory effects as specified by {@link VarHandle#addAndGet}. * * @param delta the value to add * @return the updated value */ public final long addAndGet(long delta) { - return U.getAndAddLong(this, VALUE, delta) + delta; + return (long)VALUE.addAndGet(this, delta); } /** @@ -233,12 +246,14 @@ public class AtomicLong extends Number implements java.io.Serializable { * @since 1.8 */ public final long getAndUpdate(LongUnaryOperator updateFunction) { - long prev, next; - do { - prev = get(); - next = updateFunction.applyAsLong(prev); - } while (!compareAndSet(prev, next)); - return prev; + long prev = get(), next = 0L; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.applyAsLong(prev); + if (weakCompareAndSetVolatile(prev, next)) + return prev; + haveNext = (prev == (prev = get())); + } } /** @@ -252,12 +267,14 @@ public class AtomicLong extends Number implements java.io.Serializable { * @since 1.8 */ public final long updateAndGet(LongUnaryOperator updateFunction) { - long prev, next; - do { - prev = get(); - next = updateFunction.applyAsLong(prev); - } while (!compareAndSet(prev, next)); - return next; + long prev = get(), next = 0L; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.applyAsLong(prev); + if (weakCompareAndSetVolatile(prev, next)) + return next; + haveNext = (prev == (prev = get())); + } } /** @@ -276,12 +293,14 @@ public class AtomicLong extends Number implements java.io.Serializable { */ public final long getAndAccumulate(long x, LongBinaryOperator accumulatorFunction) { - long prev, next; - do { - prev = get(); - next = accumulatorFunction.applyAsLong(prev, x); - } while (!compareAndSet(prev, next)); - return prev; + long prev = get(), next = 0L; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.applyAsLong(prev, x); + if (weakCompareAndSetVolatile(prev, next)) + return prev; + haveNext = (prev == (prev = get())); + } } /** @@ -300,12 +319,14 @@ public class AtomicLong extends Number implements java.io.Serializable { */ public final long accumulateAndGet(long x, LongBinaryOperator accumulatorFunction) { - long prev, next; - do { - prev = get(); - next = accumulatorFunction.applyAsLong(prev, x); - } while (!compareAndSet(prev, next)); - return next; + long prev = get(), next = 0L; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.applyAsLong(prev, x); + if (weakCompareAndSetVolatile(prev, next)) + return next; + haveNext = (prev == (prev = get())); + } } /** @@ -317,8 +338,9 @@ public class AtomicLong extends Number implements java.io.Serializable { } /** - * Returns the value of this {@code AtomicLong} as an {@code int} - * after a narrowing primitive conversion. + * Returns the current value of this {@code AtomicLong} as an {@code int} + * after a narrowing primitive conversion, + * with memory effects as specified by {@link VarHandle#getVolatile}. * @jls 5.1.3 Narrowing Primitive Conversions */ public int intValue() { @@ -326,7 +348,8 @@ public class AtomicLong extends Number implements java.io.Serializable { } /** - * Returns the value of this {@code AtomicLong} as a {@code long}. + * Returns the current value of this {@code AtomicLong} as a {@code long}, + * with memory effects as specified by {@link VarHandle#getVolatile}. * Equivalent to {@link #get()}. */ public long longValue() { @@ -334,8 +357,9 @@ public class AtomicLong extends Number implements java.io.Serializable { } /** - * Returns the value of this {@code AtomicLong} as a {@code float} - * after a widening primitive conversion. + * Returns the current value of this {@code AtomicLong} as a {@code float} + * after a widening primitive conversion, + * with memory effects as specified by {@link VarHandle#getVolatile}. * @jls 5.1.2 Widening Primitive Conversions */ public float floatValue() { @@ -343,12 +367,175 @@ public class AtomicLong extends Number implements java.io.Serializable { } /** - * Returns the value of this {@code AtomicLong} as a {@code double} - * after a widening primitive conversion. + * Returns the current value of this {@code AtomicLong} as a {@code double} + * after a widening primitive conversion, + * with memory effects as specified by {@link VarHandle#getVolatile}. * @jls 5.1.2 Widening Primitive Conversions */ public double doubleValue() { return (double)get(); } + // jdk9 + + /** + * Returns the current value, with memory semantics of reading as if the + * variable was declared non-{@code volatile}. + * + * @return the value + * @since 9 + */ + public final long getPlain() { + return (long)VALUE.get(this); + } + + /** + * Sets the value to {@code newValue}, with memory semantics + * of setting as if the variable was declared non-{@code volatile} + * and non-{@code final}. + * + * @param newValue the new value + * @since 9 + */ + public final void setPlain(long newValue) { + VALUE.set(this, newValue); + } + + /** + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getOpaque}. + * + * @return the value + * @since 9 + */ + public final long getOpaque() { + return (long)VALUE.getOpaque(this); + } + + /** + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setOpaque}. + * + * @param newValue the new value + * @since 9 + */ + public final void setOpaque(long newValue) { + VALUE.setOpaque(this, newValue); + } + + /** + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getAcquire}. + * + * @return the value + * @since 9 + */ + public final long getAcquire() { + return (long)VALUE.getAcquire(this); + } + + /** + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. + * + * @param newValue the new value + * @since 9 + */ + public final void setRelease(long newValue) { + VALUE.setRelease(this, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchange}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final long compareAndExchange(long expectedValue, long newValue) { + return (long)VALUE.compareAndExchange(this, expectedValue, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeAcquire}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final long compareAndExchangeAcquire(long expectedValue, long newValue) { + return (long)VALUE.compareAndExchangeAcquire(this, expectedValue, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeRelease}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final long compareAndExchangeRelease(long expectedValue, long newValue) { + return (long)VALUE.compareAndExchangeRelease(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetVolatile}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetVolatile(long expectedValue, long newValue) { + return VALUE.weakCompareAndSetVolatile(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetAcquire}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetAcquire(long expectedValue, long newValue) { + return VALUE.weakCompareAndSetAcquire(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetRelease}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetRelease(long expectedValue, long newValue) { + return VALUE.weakCompareAndSetRelease(this, expectedValue, newValue); + } + } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java index 041e561b2a7..3d584cf44e0 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java @@ -35,43 +35,24 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.function.LongBinaryOperator; import java.util.function.LongUnaryOperator; /** * A {@code long} array in which elements may be updated atomically. - * See the {@link java.util.concurrent.atomic} package specification - * for description of the properties of atomic variables. + * See the {@link VarHandle} specification for descriptions of the + * properties of atomic accesses. * @since 1.5 * @author Doug Lea */ public class AtomicLongArray implements java.io.Serializable { private static final long serialVersionUID = -2308431214976778248L; - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final int ABASE; - private static final int ASHIFT; + private static final VarHandle AA + = MethodHandles.arrayElementVarHandle(long[].class); private final long[] array; - static { - ABASE = U.arrayBaseOffset(long[].class); - int scale = U.arrayIndexScale(long[].class); - if ((scale & (scale - 1)) != 0) - throw new Error("array index scale not a power of two"); - ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); - } - - private long checkedByteOffset(int i) { - if (i < 0 || i >= array.length) - throw new IndexOutOfBoundsException("index " + i); - - return byteOffset(i); - } - - private static long byteOffset(int i) { - return ((long) i << ASHIFT) + ABASE; - } - /** * Creates a new AtomicLongArray of the given length, with all * elements initially zero. @@ -104,147 +85,155 @@ public class AtomicLongArray implements java.io.Serializable { } /** - * Gets the current value at position {@code i}. + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getVolatile}. * * @param i the index * @return the current value */ public final long get(int i) { - return getRaw(checkedByteOffset(i)); - } - - private long getRaw(long offset) { - return U.getLongVolatile(array, offset); + return (long)AA.getVolatile(array, i); } /** - * Sets the element at position {@code i} to the given value. + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setVolatile}. * * @param i the index * @param newValue the new value */ public final void set(int i, long newValue) { - U.putLongVolatile(array, checkedByteOffset(i), newValue); + AA.setVolatile(array, i, newValue); } /** - * Eventually sets the element at position {@code i} to the given value. + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. * * @param i the index * @param newValue the new value * @since 1.6 */ public final void lazySet(int i, long newValue) { - U.putLongRelease(array, checkedByteOffset(i), newValue); + AA.setRelease(array, i, newValue); } /** - * Atomically sets the element at position {@code i} to the given value - * and returns the old value. + * Atomically sets the element at index {@code i} to {@code + * newValue} and returns the old value, + * with memory effects as specified by {@link VarHandle#getAndSet}. * * @param i the index * @param newValue the new value * @return the previous value */ public final long getAndSet(int i, long newValue) { - return U.getAndSetLong(array, checkedByteOffset(i), newValue); + return (long)AA.getAndSet(array, i, newValue); } /** - * Atomically sets the element at position {@code i} to the given - * updated value if the current value {@code ==} the expected value. + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#compareAndSet}. * * @param i the index - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ - public final boolean compareAndSet(int i, long expect, long update) { - return compareAndSetRaw(checkedByteOffset(i), expect, update); - } - - private boolean compareAndSetRaw(long offset, long expect, long update) { - return U.compareAndSwapLong(array, offset, expect, update); + public final boolean compareAndSet(int i, long expectedValue, long newValue) { + return AA.compareAndSet(array, i, expectedValue, newValue); } /** - * 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 appropriate alternative to {@code compareAndSet}. + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#weakCompareAndSet}. * * @param i the index - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful */ - public final boolean weakCompareAndSet(int i, long expect, long update) { - return compareAndSet(i, expect, update); + public final boolean weakCompareAndSet(int i, long expectedValue, long newValue) { + return AA.weakCompareAndSet(array, i, expectedValue, newValue); } /** - * Atomically increments by one the element at index {@code i}. + * Atomically increments the value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAndAdd}. + * + *

Equivalent to {@code getAndAdd(i, 1)}. * * @param i the index * @return the previous value */ public final long getAndIncrement(int i) { - return getAndAdd(i, 1); + return (long)AA.getAndAdd(array, i, 1L); } /** - * Atomically decrements by one the element at index {@code i}. + * Atomically decrements the value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAndAdd}. + * + *

Equivalent to {@code getAndAdd(i, -1)}. * * @param i the index * @return the previous value */ public final long getAndDecrement(int i) { - return getAndAdd(i, -1); + return (long)AA.getAndAdd(array, i, -1L); } /** - * Atomically adds the given value to the element at index {@code i}. + * Atomically adds the given value to the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAndAdd}. * * @param i the index * @param delta the value to add * @return the previous value */ public final long getAndAdd(int i, long delta) { - return U.getAndAddLong(array, checkedByteOffset(i), delta); + return (long)AA.getAndAdd(array, i, delta); } /** - * Atomically increments by one the element at index {@code i}. + * Atomically increments the value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#addAndGet}. + * + *

Equivalent to {@code addAndGet(i, 1)}. * * @param i the index * @return the updated value */ public final long incrementAndGet(int i) { - return getAndAdd(i, 1) + 1; + return (long)AA.addAndGet(array, i, 1L); } /** - * Atomically decrements by one the element at index {@code i}. + * Atomically decrements the value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#addAndGet}. + * + *

Equivalent to {@code addAndGet(i, -1)}. * * @param i the index * @return the updated value */ public final long decrementAndGet(int i) { - return getAndAdd(i, -1) - 1; + return (long)AA.addAndGet(array, i, -1L); } /** - * Atomically adds the given value to the element at index {@code i}. + * Atomically adds the given value to the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#addAndGet}. * * @param i the index * @param delta the value to add * @return the updated value */ public long addAndGet(int i, long delta) { - return getAndAdd(i, delta) + delta; + return (long)AA.addAndGet(array, i, delta); } /** @@ -259,13 +248,14 @@ public class AtomicLongArray implements java.io.Serializable { * @since 1.8 */ public final long getAndUpdate(int i, LongUnaryOperator updateFunction) { - long offset = checkedByteOffset(i); - long prev, next; - do { - prev = getRaw(offset); - next = updateFunction.applyAsLong(prev); - } while (!compareAndSetRaw(offset, prev, next)); - return prev; + long prev = get(i), next = 0L; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.applyAsLong(prev); + if (weakCompareAndSetVolatile(i, prev, next)) + return prev; + haveNext = (prev == (prev = get(i))); + } } /** @@ -280,23 +270,25 @@ public class AtomicLongArray implements java.io.Serializable { * @since 1.8 */ public final long updateAndGet(int i, LongUnaryOperator updateFunction) { - long offset = checkedByteOffset(i); - long prev, next; - do { - prev = getRaw(offset); - next = updateFunction.applyAsLong(prev); - } while (!compareAndSetRaw(offset, prev, next)); - return next; + long prev = get(i), next = 0L; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.applyAsLong(prev); + if (weakCompareAndSetVolatile(i, prev, next)) + return next; + haveNext = (prev == (prev = get(i))); + } } /** * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the previous value. The function should - * be side-effect-free, since it may be re-applied when attempted + * results of applying the given function to the current and given + * values, returning the previous value. The function should be + * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. + * applied with the current value of the element at index {@code i} + * as its first argument, and the given update as the second + * argument. * * @param i the index * @param x the update value @@ -306,23 +298,25 @@ public class AtomicLongArray implements java.io.Serializable { */ public final long getAndAccumulate(int i, long x, LongBinaryOperator accumulatorFunction) { - long offset = checkedByteOffset(i); - long prev, next; - do { - prev = getRaw(offset); - next = accumulatorFunction.applyAsLong(prev, x); - } while (!compareAndSetRaw(offset, prev, next)); - return prev; + long prev = get(i), next = 0L; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.applyAsLong(prev, x); + if (weakCompareAndSetVolatile(i, prev, next)) + return prev; + haveNext = (prev == (prev = get(i))); + } } /** * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the updated value. The function should - * be side-effect-free, since it may be re-applied when attempted + * results of applying the given function to the current and given + * values, returning the updated value. The function should be + * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. + * applied with the current value of the element at index {@code i} + * as its first argument, and the given update as the second + * argument. * * @param i the index * @param x the update value @@ -332,13 +326,14 @@ public class AtomicLongArray implements java.io.Serializable { */ public final long accumulateAndGet(int i, long x, LongBinaryOperator accumulatorFunction) { - long offset = checkedByteOffset(i); - long prev, next; - do { - prev = getRaw(offset); - next = accumulatorFunction.applyAsLong(prev, x); - } while (!compareAndSetRaw(offset, prev, next)); - return next; + long prev = get(i), next = 0L; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.applyAsLong(prev, x); + if (weakCompareAndSetVolatile(i, prev, next)) + return next; + haveNext = (prev == (prev = get(i))); + } } /** @@ -353,11 +348,189 @@ public class AtomicLongArray implements java.io.Serializable { StringBuilder b = new StringBuilder(); b.append('['); for (int i = 0; ; i++) { - b.append(getRaw(byteOffset(i))); + b.append(get(i)); if (i == iMax) return b.append(']').toString(); b.append(',').append(' '); } } + // jdk9 + + /** + * Returns the current value of the element at index {@code i}, + * with memory semantics of reading as if the variable was declared + * non-{@code volatile}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final long getPlain(int i) { + return (long)AA.get(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory semantics of setting as if the variable was + * declared non-{@code volatile} and non-{@code final}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setPlain(int i, long newValue) { + AA.set(array, i, newValue); + } + + /** + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getOpaque}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final long getOpaque(int i) { + return (long)AA.getOpaque(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setOpaque}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setOpaque(int i, long newValue) { + AA.setOpaque(array, i, newValue); + } + + /** + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAcquire}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final long getAcquire(int i) { + return (long)AA.getAcquire(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setRelease(int i, long newValue) { + AA.setRelease(array, i, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchange}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final long compareAndExchange(int i, long expectedValue, long newValue) { + return (long)AA.compareAndExchange(array, i, expectedValue, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeAcquire}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final long compareAndExchangeAcquire(int i, long expectedValue, long newValue) { + return (long)AA.compareAndExchangeAcquire(array, i, expectedValue, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeRelease}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final long compareAndExchangeRelease(int i, long expectedValue, long newValue) { + return (long)AA.compareAndExchangeRelease(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetVolatile}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetVolatile(int i, long expectedValue, long newValue) { + return AA.weakCompareAndSetVolatile(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetAcquire}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetAcquire(int i, long expectedValue, long newValue) { + return AA.weakCompareAndSetAcquire(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetRelease}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetRelease(int i, long expectedValue, long newValue) { + return AA.weakCompareAndSetRelease(array, i, expectedValue, newValue); + } + } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java index c3ad0afff21..af39d8a4603 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java @@ -42,6 +42,7 @@ import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.function.LongBinaryOperator; import java.util.function.LongUnaryOperator; +import jdk.internal.misc.Unsafe; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; @@ -153,8 +154,8 @@ public abstract class AtomicLongFieldUpdater { public abstract void lazySet(T obj, long newValue); /** - * Gets the current value held in the field of the given object managed - * by this updater. + * Returns the current value held in the field of the given object + * managed by this updater. * * @param obj An object whose field to get * @return the current value @@ -366,7 +367,7 @@ public abstract class AtomicLongFieldUpdater { } private static final class CASUpdater extends AtomicLongFieldUpdater { - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); + private static final Unsafe U = Unsafe.getUnsafe(); private final long offset; /** * if field is protected, the subclass constructing updater, else @@ -497,7 +498,7 @@ public abstract class AtomicLongFieldUpdater { } private static final class LockedUpdater extends AtomicLongFieldUpdater { - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); + private static final Unsafe U = Unsafe.getUnsafe(); private final long offset; /** * if field is protected, the subclass constructing updater, else diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java index 8204c3fcc61..367808c7192 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java @@ -35,6 +35,9 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; + /** * An {@code AtomicMarkableReference} maintains an object reference * along with a mark bit, that can be updated atomically. @@ -188,20 +191,19 @@ public class AtomicMarkableReference { casPair(current, Pair.of(expectedReference, newMark))); } - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long PAIR; + // VarHandle mechanics + private static final VarHandle PAIR; static { try { - PAIR = U.objectFieldOffset - (AtomicMarkableReference.class.getDeclaredField("pair")); + MethodHandles.Lookup l = MethodHandles.lookup(); + PAIR = l.findVarHandle(AtomicMarkableReference.class, "pair", + Pair.class); } catch (ReflectiveOperationException e) { throw new Error(e); } } private boolean casPair(Pair cmp, Pair val) { - return U.compareAndSwapObject(this, PAIR, cmp, val); + return PAIR.compareAndSet(this, cmp, val); } } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java index d0a0b0a90cf..27b5bed586a 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java @@ -35,33 +35,32 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.function.BinaryOperator; import java.util.function.UnaryOperator; /** - * An object reference that may be updated atomically. See the {@link - * java.util.concurrent.atomic} package specification for description - * of the properties of atomic variables. + * An object reference that may be updated atomically. See the {@link + * VarHandle} specification for descriptions of the properties of + * atomic accesses. * @since 1.5 * @author Doug Lea * @param The type of object referred to by this reference */ public class AtomicReference implements java.io.Serializable { private static final long serialVersionUID = -1848883965231344442L; - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long VALUE; - + private static final VarHandle VALUE; static { try { - VALUE = U.objectFieldOffset - (AtomicReference.class.getDeclaredField("value")); + MethodHandles.Lookup l = MethodHandles.lookup(); + VALUE = l.findVarHandle(AtomicReference.class, "value", Object.class); } catch (ReflectiveOperationException e) { throw new Error(e); } } - private volatile V value; + private volatile Object value; /** * Creates a new AtomicReference with the given initial value. @@ -79,16 +78,19 @@ public class AtomicReference implements java.io.Serializable { } /** - * Gets the current value. + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getVolatile}. * * @return the current value */ + @SuppressWarnings("unchecked") public final V get() { - return value; + return (V)value; } /** - * Sets to the given value. + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setVolatile}. * * @param newValue the new value */ @@ -97,52 +99,53 @@ public class AtomicReference implements java.io.Serializable { } /** - * Eventually sets to the given value. + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. * * @param newValue the new value * @since 1.6 */ public final void lazySet(V newValue) { - U.putObjectRelease(this, VALUE, newValue); + VALUE.setRelease(this, newValue); } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. - * @param expect the expected value - * @param update the new value + * Atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#compareAndSet}. + * + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ - public final boolean compareAndSet(V expect, V update) { - return U.compareAndSwapObject(this, VALUE, expect, update); + public final boolean compareAndSet(V expectedValue, V newValue) { + return VALUE.compareAndSet(this, expectedValue, newValue); } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#weakCompareAndSet}. * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. - * - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful */ - public final boolean weakCompareAndSet(V expect, V update) { - return U.compareAndSwapObject(this, VALUE, expect, update); + public final boolean weakCompareAndSet(V expectedValue, V newValue) { + return VALUE.weakCompareAndSet(this, expectedValue, newValue); } /** - * Atomically sets to the given value and returns the old value. + * Atomically sets the value to {@code newValue} and returns the old value, + * with memory effects as specified by {@link VarHandle#getAndSet}. * * @param newValue the new value * @return the previous value */ @SuppressWarnings("unchecked") public final V getAndSet(V newValue) { - return (V)U.getAndSetObject(this, VALUE, newValue); + return (V)VALUE.getAndSet(this, newValue); } /** @@ -156,12 +159,14 @@ public class AtomicReference implements java.io.Serializable { * @since 1.8 */ public final V getAndUpdate(UnaryOperator updateFunction) { - V prev, next; - do { - prev = get(); - next = updateFunction.apply(prev); - } while (!compareAndSet(prev, next)); - return prev; + V prev = get(), next = null; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.apply(prev); + if (weakCompareAndSetVolatile(prev, next)) + return prev; + haveNext = (prev == (prev = get())); + } } /** @@ -175,12 +180,14 @@ public class AtomicReference implements java.io.Serializable { * @since 1.8 */ public final V updateAndGet(UnaryOperator updateFunction) { - V prev, next; - do { - prev = get(); - next = updateFunction.apply(prev); - } while (!compareAndSet(prev, next)); - return next; + V prev = get(), next = null; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.apply(prev); + if (weakCompareAndSetVolatile(prev, next)) + return next; + haveNext = (prev == (prev = get())); + } } /** @@ -199,12 +206,14 @@ public class AtomicReference implements java.io.Serializable { */ public final V getAndAccumulate(V x, BinaryOperator accumulatorFunction) { - V prev, next; - do { - prev = get(); - next = accumulatorFunction.apply(prev, x); - } while (!compareAndSet(prev, next)); - return prev; + V prev = get(), next = null; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.apply(prev, x); + if (weakCompareAndSetVolatile(prev, next)) + return prev; + haveNext = (prev == (prev = get())); + } } /** @@ -223,12 +232,14 @@ public class AtomicReference implements java.io.Serializable { */ public final V accumulateAndGet(V x, BinaryOperator accumulatorFunction) { - V prev, next; - do { - prev = get(); - next = accumulatorFunction.apply(prev, x); - } while (!compareAndSet(prev, next)); - return next; + V prev = get(), next = null; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.apply(prev, x); + if (weakCompareAndSetVolatile(prev, next)) + return next; + haveNext = (prev == (prev = get())); + } } /** @@ -239,4 +250,166 @@ public class AtomicReference implements java.io.Serializable { return String.valueOf(get()); } + // jdk9 + + /** + * Returns the current value, with memory semantics of reading as + * if the variable was declared non-{@code volatile}. + * + * @return the value + * @since 9 + */ + public final V getPlain() { + return (V)VALUE.get(this); + } + + /** + * Sets the value to {@code newValue}, with memory semantics + * of setting as if the variable was declared non-{@code volatile} + * and non-{@code final}. + * + * @param newValue the new value + * @since 9 + */ + public final void setPlain(V newValue) { + VALUE.set(this, newValue); + } + + /** + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getOpaque}. + * + * @return the value + * @since 9 + */ + public final V getOpaque() { + return (V)VALUE.getOpaque(this); + } + + /** + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setOpaque}. + * + * @param newValue the new value + * @since 9 + */ + public final void setOpaque(V newValue) { + VALUE.setOpaque(this, newValue); + } + + /** + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getAcquire}. + * + * @return the value + * @since 9 + */ + public final V getAcquire() { + return (V)VALUE.getAcquire(this); + } + + /** + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. + * + * @param newValue the new value + * @since 9 + */ + public final void setRelease(V newValue) { + VALUE.setRelease(this, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchange}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final V compareAndExchange(V expectedValue, V newValue) { + return (V)VALUE.compareAndExchange(this, expectedValue, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeAcquire}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final V compareAndExchangeAcquire(V expectedValue, V newValue) { + return (V)VALUE.compareAndExchangeAcquire(this, expectedValue, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeRelease}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final V compareAndExchangeRelease(V expectedValue, V newValue) { + return (V)VALUE.compareAndExchangeRelease(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetVolatile}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetVolatile(V expectedValue, V newValue) { + return VALUE.weakCompareAndSetVolatile(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetAcquire}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetAcquire(V expectedValue, V newValue) { + return VALUE.weakCompareAndSetAcquire(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetRelease}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetRelease(V expectedValue, V newValue) { + return VALUE.weakCompareAndSetRelease(this, expectedValue, newValue); + } + } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java index a2e8b2b7805..34bb0f135da 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java @@ -35,54 +35,28 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.lang.reflect.Array; +import java.lang.reflect.Field; import java.util.Arrays; import java.util.function.BinaryOperator; import java.util.function.UnaryOperator; /** * An array of object references in which elements may be updated - * atomically. See the {@link java.util.concurrent.atomic} package - * specification for description of the properties of atomic - * variables. + * atomically. See the {@link VarHandle} specification for + * descriptions of the properties of atomic accesses. * @since 1.5 * @author Doug Lea * @param The base class of elements held in this array */ public class AtomicReferenceArray implements java.io.Serializable { private static final long serialVersionUID = -6209656149925076980L; - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long ARRAY; - private static final int ABASE; - private static final int ASHIFT; + private static final VarHandle AA + = MethodHandles.arrayElementVarHandle(Object[].class); private final Object[] array; // must have exact type Object[] - static { - try { - ARRAY = U.objectFieldOffset - (AtomicReferenceArray.class.getDeclaredField("array")); - ABASE = U.arrayBaseOffset(Object[].class); - int scale = U.arrayIndexScale(Object[].class); - if ((scale & (scale - 1)) != 0) - throw new Error("array index scale not a power of two"); - ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); - } catch (ReflectiveOperationException e) { - throw new Error(e); - } - } - - private long checkedByteOffset(int i) { - if (i < 0 || i >= array.length) - throw new IndexOutOfBoundsException("index " + i); - - return byteOffset(i); - } - - private static long byteOffset(int i) { - return ((long) i << ASHIFT) + ABASE; - } - /** * Creates a new AtomicReferenceArray of the given length, with all * elements initially null. @@ -115,44 +89,44 @@ public class AtomicReferenceArray implements java.io.Serializable { } /** - * Gets the current value at position {@code i}. + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getVolatile}. * * @param i the index * @return the current value */ - public final E get(int i) { - return getRaw(checkedByteOffset(i)); - } - @SuppressWarnings("unchecked") - private E getRaw(long offset) { - return (E) U.getObjectVolatile(array, offset); + public final E get(int i) { + return (E)AA.getVolatile(array, i); } /** - * Sets the element at position {@code i} to the given value. + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setVolatile}. * * @param i the index * @param newValue the new value */ public final void set(int i, E newValue) { - U.putObjectVolatile(array, checkedByteOffset(i), newValue); + AA.setVolatile(array, i, newValue); } /** - * Eventually sets the element at position {@code i} to the given value. + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. * * @param i the index * @param newValue the new value * @since 1.6 */ public final void lazySet(int i, E newValue) { - U.putObjectRelease(array, checkedByteOffset(i), newValue); + AA.setRelease(array, i, newValue); } /** - * Atomically sets the element at position {@code i} to the given - * value and returns the old value. + * Atomically sets the element at index {@code i} to {@code + * newValue} and returns the old value, + * with memory effects as specified by {@link VarHandle#getAndSet}. * * @param i the index * @param newValue the new value @@ -160,42 +134,36 @@ public class AtomicReferenceArray implements java.io.Serializable { */ @SuppressWarnings("unchecked") public final E getAndSet(int i, E newValue) { - return (E)U.getAndSetObject(array, checkedByteOffset(i), newValue); + return (E)AA.getAndSet(array, i, newValue); } /** - * Atomically sets the element at position {@code i} to the given - * updated value if the current value {@code ==} the expected value. + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#compareAndSet}. * * @param i the index - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ - public final boolean compareAndSet(int i, E expect, E update) { - return compareAndSetRaw(checkedByteOffset(i), expect, update); - } - - private boolean compareAndSetRaw(long offset, E expect, E update) { - return U.compareAndSwapObject(array, offset, expect, update); + public final boolean compareAndSet(int i, E expectedValue, E newValue) { + return AA.compareAndSet(array, i, expectedValue, newValue); } /** - * 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 appropriate alternative to {@code compareAndSet}. + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#weakCompareAndSet}. * * @param i the index - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful */ - public final boolean weakCompareAndSet(int i, E expect, E update) { - return compareAndSet(i, expect, update); + public final boolean weakCompareAndSet(int i, E expectedValue, E newValue) { + return AA.weakCompareAndSet(array, i, expectedValue, newValue); } /** @@ -210,13 +178,14 @@ public class AtomicReferenceArray implements java.io.Serializable { * @since 1.8 */ public final E getAndUpdate(int i, UnaryOperator updateFunction) { - long offset = checkedByteOffset(i); - E prev, next; - do { - prev = getRaw(offset); - next = updateFunction.apply(prev); - } while (!compareAndSetRaw(offset, prev, next)); - return prev; + E prev = get(i), next = null; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.apply(prev); + if (weakCompareAndSetVolatile(i, prev, next)) + return prev; + haveNext = (prev == (prev = get(i))); + } } /** @@ -231,23 +200,25 @@ public class AtomicReferenceArray implements java.io.Serializable { * @since 1.8 */ public final E updateAndGet(int i, UnaryOperator updateFunction) { - long offset = checkedByteOffset(i); - E prev, next; - do { - prev = getRaw(offset); - next = updateFunction.apply(prev); - } while (!compareAndSetRaw(offset, prev, next)); - return next; + E prev = get(i), next = null; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.apply(prev); + if (weakCompareAndSetVolatile(i, prev, next)) + return next; + haveNext = (prev == (prev = get(i))); + } } /** * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the previous value. The function should - * be side-effect-free, since it may be re-applied when attempted + * results of applying the given function to the current and given + * values, returning the previous value. The function should be + * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. + * applied with the current value of the element at index {@code i} + * as its first argument, and the given update as the second + * argument. * * @param i the index * @param x the update value @@ -257,23 +228,25 @@ public class AtomicReferenceArray implements java.io.Serializable { */ public final E getAndAccumulate(int i, E x, BinaryOperator accumulatorFunction) { - long offset = checkedByteOffset(i); - E prev, next; - do { - prev = getRaw(offset); - next = accumulatorFunction.apply(prev, x); - } while (!compareAndSetRaw(offset, prev, next)); - return prev; + E prev = get(i), next = null; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.apply(prev, x); + if (weakCompareAndSetVolatile(i, prev, next)) + return prev; + haveNext = (prev == (prev = get(i))); + } } /** * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the updated value. The function should - * be side-effect-free, since it may be re-applied when attempted + * results of applying the given function to the current and given + * values, returning the updated value. The function should be + * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. + * applied with the current value of the element at index {@code i} + * as its first argument, and the given update as the second + * argument. * * @param i the index * @param x the update value @@ -283,13 +256,14 @@ public class AtomicReferenceArray implements java.io.Serializable { */ public final E accumulateAndGet(int i, E x, BinaryOperator accumulatorFunction) { - long offset = checkedByteOffset(i); - E prev, next; - do { - prev = getRaw(offset); - next = accumulatorFunction.apply(prev, x); - } while (!compareAndSetRaw(offset, prev, next)); - return next; + E prev = get(i), next = null; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.apply(prev, x); + if (weakCompareAndSetVolatile(i, prev, next)) + return next; + haveNext = (prev == (prev = get(i))); + } } /** @@ -304,7 +278,7 @@ public class AtomicReferenceArray implements java.io.Serializable { StringBuilder b = new StringBuilder(); b.append('['); for (int i = 0; ; i++) { - b.append(getRaw(byteOffset(i))); + b.append(get(i)); if (i == iMax) return b.append(']').toString(); b.append(',').append(' '); @@ -326,7 +300,199 @@ public class AtomicReferenceArray implements java.io.Serializable { throw new java.io.InvalidObjectException("Not array type"); if (a.getClass() != Object[].class) a = Arrays.copyOf((Object[])a, Array.getLength(a), Object[].class); - U.putObjectVolatile(this, ARRAY, a); + Field arrayField = java.security.AccessController.doPrivileged( + (java.security.PrivilegedAction) () -> { + try { + Field f = AtomicReferenceArray.class + .getDeclaredField("array"); + f.setAccessible(true); + return f; + } catch (ReflectiveOperationException e) { + throw new Error(e); + }}); + try { + arrayField.set(this, a); + } catch (IllegalAccessException e) { + throw new Error(e); + } + } + + // jdk9 + + /** + * Returns the current value of the element at index {@code i}, + * with memory semantics of reading as if the variable was declared + * non-{@code volatile}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final E getPlain(int i) { + return (E)AA.get(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory semantics of setting as if the variable was + * declared non-{@code volatile} and non-{@code final}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setPlain(int i, E newValue) { + AA.set(array, i, newValue); + } + + /** + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getOpaque}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final E getOpaque(int i) { + return (E)AA.getOpaque(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setOpaque}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setOpaque(int i, E newValue) { + AA.setOpaque(array, i, newValue); + } + + /** + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAcquire}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final E getAcquire(int i) { + return (E)AA.getAcquire(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setRelease(int i, E newValue) { + AA.setRelease(array, i, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchange}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final E compareAndExchange(int i, E expectedValue, E newValue) { + return (E)AA.compareAndExchange(array, i, expectedValue, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeAcquire}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final E compareAndExchangeAcquire(int i, E expectedValue, E newValue) { + return (E)AA.compareAndExchangeAcquire(array, i, expectedValue, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeRelease}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final E compareAndExchangeRelease(int i, E expectedValue, E newValue) { + return (E)AA.compareAndExchangeRelease(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetVolatile}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetVolatile(int i, E expectedValue, E newValue) { + return AA.weakCompareAndSetVolatile(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetAcquire}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetAcquire(int i, E expectedValue, E newValue) { + return AA.weakCompareAndSetAcquire(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetRelease}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetRelease(int i, E expectedValue, E newValue) { + return AA.weakCompareAndSetRelease(array, i, expectedValue, newValue); } } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java index f6dbbe36479..faacb7fc432 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java @@ -42,6 +42,7 @@ import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.function.BinaryOperator; import java.util.function.UnaryOperator; +import jdk.internal.misc.Unsafe; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; @@ -168,8 +169,8 @@ public abstract class AtomicReferenceFieldUpdater { public abstract void lazySet(T obj, V newValue); /** - * Gets the current value held in the field of the given object managed - * by this updater. + * Returns the current value held in the field of the given object + * managed by this updater. * * @param obj An object whose field to get * @return the current value @@ -284,7 +285,7 @@ public abstract class AtomicReferenceFieldUpdater { private static final class AtomicReferenceFieldUpdaterImpl extends AtomicReferenceFieldUpdater { - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); + private static final Unsafe U = Unsafe.getUnsafe(); private final long offset; /** * if field is protected, the subclass constructing updater, else diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java index fd520d7d889..f031fe81d81 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java @@ -35,6 +35,9 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; + /** * An {@code AtomicStampedReference} maintains an object reference * along with an integer "stamp", that can be updated atomically. @@ -188,20 +191,19 @@ public class AtomicStampedReference { casPair(current, Pair.of(expectedReference, newStamp))); } - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long PAIR; + // VarHandle mechanics + private static final VarHandle PAIR; static { try { - PAIR = U.objectFieldOffset - (AtomicStampedReference.class.getDeclaredField("pair")); + MethodHandles.Lookup l = MethodHandles.lookup(); + PAIR = l.findVarHandle(AtomicStampedReference.class, "pair", + Pair.class); } catch (ReflectiveOperationException e) { throw new Error(e); } } private boolean casPair(Pair cmp, Pair val) { - return U.compareAndSwapObject(this, PAIR, cmp, val); + return PAIR.compareAndSet(this, cmp, val); } } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java index 0e9a8f5927b..939a4a2eed9 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java @@ -68,7 +68,7 @@ import java.util.function.LongBinaryOperator; *

Class {@link LongAdder} provides analogs of the functionality of * this class for the common special case of maintaining counts and * sums. The call {@code new LongAdder()} is equivalent to {@code new - * LongAccumulator((x, y) -> x + y, 0L}. + * LongAccumulator((x, y) -> x + y, 0L)}. * *

This class extends {@link Number}, but does not define * methods such as {@code equals}, {@code hashCode} and {@code diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java index 77ee89a614b..a70094e3623 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java @@ -35,10 +35,13 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.Arrays; import java.util.concurrent.ThreadLocalRandom; import java.util.function.DoubleBinaryOperator; import java.util.function.LongBinaryOperator; +import jdk.internal.misc.Unsafe; /** * A package-local class holding common representation and mechanics @@ -123,22 +126,21 @@ abstract class Striped64 extends Number { volatile long value; Cell(long x) { value = x; } final boolean cas(long cmp, long val) { - return U.compareAndSwapLong(this, VALUE, cmp, val); + return VALUE.compareAndSet(this, cmp, val); } final void reset() { - U.putLongVolatile(this, VALUE, 0L); + VALUE.setVolatile(this, 0L); } final void reset(long identity) { - U.putLongVolatile(this, VALUE, identity); + VALUE.setVolatile(this, identity); } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long VALUE; + // VarHandle mechanics + private static final VarHandle VALUE; static { try { - VALUE = U.objectFieldOffset - (Cell.class.getDeclaredField("value")); + MethodHandles.Lookup l = MethodHandles.lookup(); + VALUE = l.findVarHandle(Cell.class, "value", long.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -174,14 +176,14 @@ abstract class Striped64 extends Number { * CASes the base field. */ final boolean casBase(long cmp, long val) { - return U.compareAndSwapLong(this, BASE, cmp, val); + return BASE.compareAndSet(this, cmp, val); } /** * CASes the cellsBusy field from 0 to 1 to acquire lock. */ final boolean casCellsBusy() { - return U.compareAndSwapInt(this, CELLSBUSY, 0, 1); + return CELLSBUSY.compareAndSet(this, 0, 1); } /** @@ -371,18 +373,16 @@ abstract class Striped64 extends Number { } } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long BASE; - private static final long CELLSBUSY; + // Unsafe and VarHandle mechanics + private static final Unsafe U = Unsafe.getUnsafe(); + private static final VarHandle BASE; + private static final VarHandle CELLSBUSY; private static final long PROBE; static { try { - BASE = U.objectFieldOffset - (Striped64.class.getDeclaredField("base")); - CELLSBUSY = U.objectFieldOffset - (Striped64.class.getDeclaredField("cellsBusy")); - + MethodHandles.Lookup l = MethodHandles.lookup(); + BASE = l.findVarHandle(Striped64.class, "base", long.class); + CELLSBUSY = l.findVarHandle(Striped64.class, "cellsBusy", int.class); PROBE = U.objectFieldOffset (Thread.class.getDeclaredField("threadLocalRandomProbe")); } catch (ReflectiveOperationException e) { diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/package-info.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/package-info.java index a8e1ff34528..b0ff27f96e3 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/package-info.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/package-info.java @@ -35,26 +35,10 @@ /** * A small toolkit of classes that support lock-free thread-safe - * programming on single variables. In essence, the classes in this - * package extend the notion of {@code volatile} values, fields, and - * array elements to those that also provide an atomic conditional update - * operation of the form: - * - *

 {@code boolean compareAndSet(expectedValue, updateValue);}
- * - *

This method (which varies in argument types across different - * classes) atomically sets a variable to the {@code updateValue} if it - * currently holds the {@code expectedValue}, reporting {@code true} on - * success. The classes in this package also contain methods to get and - * unconditionally set values, as well as a weaker conditional atomic - * update operation {@code weakCompareAndSet} described below. - * - *

The specifications of these methods enable implementations to - * employ efficient machine-level atomic instructions that are available - * on contemporary processors. However on some platforms, support may - * entail some form of internal locking. Thus the methods are not - * strictly guaranteed to be non-blocking -- - * a thread may block transiently before performing the operation. + * programming on single variables. Instances of Atomic classes + * maintain values that are accessed and updated using methods + * otherwise available for fields using associated atomic {@link + * java.lang.invoke.VarHandle} operations. * *

Instances of classes * {@link java.util.concurrent.atomic.AtomicBoolean}, @@ -92,45 +76,26 @@ * return prev; // return next; for transformAndGet * }} * - *

The memory effects for accesses and updates of atomics generally - * follow the rules for volatiles, as stated in - * - * Chapter 17 of - * The Java™ Language Specification: + *

These classes are not general purpose replacements for {@code + * java.lang.Integer} and related classes. They do not + * define methods such as {@code equals}, {@code hashCode} and {@code + * compareTo}. Because atomic variables are expected to be mutated, + * they are poor choices for hash table keys. * - *

    - * - *
  • {@code get} has the memory effects of reading a - * {@code volatile} variable. - * - *
  • {@code set} has the memory effects of writing (assigning) a - * {@code volatile} variable. - * - *
  • {@code lazySet} has the memory effects of writing (assigning) - * a {@code volatile} variable except that it permits reorderings with - * subsequent (but not previous) memory actions that do not themselves - * impose reordering constraints with ordinary non-{@code volatile} - * writes. Among other usage contexts, {@code lazySet} may apply when - * nulling out, for the sake of garbage collection, a reference that is - * never accessed again. - * - *
  • {@code weakCompareAndSet} atomically reads and conditionally - * writes a variable but does not - * create any happens-before orderings, so provides no guarantees - * with respect to previous or subsequent reads and writes of any - * variables other than the target of the {@code weakCompareAndSet}. - * - *
  • {@code compareAndSet} - * and all other read-and-update operations such as {@code getAndIncrement} - * have the memory effects of both reading and - * writing {@code volatile} variables. - *
- * - *

In addition to classes representing single values, this package - * contains Updater classes that can be used to obtain - * {@code compareAndSet} operations on any selected {@code volatile} - * field of any selected class. + *

The + * {@link java.util.concurrent.atomic.AtomicIntegerArray}, + * {@link java.util.concurrent.atomic.AtomicLongArray}, and + * {@link java.util.concurrent.atomic.AtomicReferenceArray} classes + * further extend atomic operation support to arrays of these types. + * These classes are also notable in providing {@code volatile} access + * semantics for their array elements. * + *

In addition to classes representing single values and arrays, + * this package contains Updater classes that can be used to + * obtain {@code compareAndSet} and related operations on any selected + * {@code volatile} field of any selected class. These classes + * predate the introduction of {@link + * java.lang.invoke.VarHandle}, and are of more limited use. * {@link java.util.concurrent.atomic.AtomicReferenceFieldUpdater}, * {@link java.util.concurrent.atomic.AtomicIntegerFieldUpdater}, and * {@link java.util.concurrent.atomic.AtomicLongFieldUpdater} are @@ -143,38 +108,6 @@ * reflection-based setup, less convenient usage, and weaker * guarantees. * - *

The - * {@link java.util.concurrent.atomic.AtomicIntegerArray}, - * {@link java.util.concurrent.atomic.AtomicLongArray}, and - * {@link java.util.concurrent.atomic.AtomicReferenceArray} classes - * further extend atomic operation support to arrays of these types. - * These classes are also notable in providing {@code volatile} access - * semantics for their array elements, which is not supported for - * ordinary arrays. - * - *

The atomic classes also support method - * {@code weakCompareAndSet}, which has limited applicability. On some - * platforms, the weak version may be more efficient than {@code - * compareAndSet} in the normal case, but differs in that any given - * invocation of the {@code weakCompareAndSet} method may return {@code - * false} spuriously (that is, for no apparent reason). A - * {@code false} return means only that the operation may be retried if - * desired, relying on the guarantee that repeated invocation when the - * variable holds {@code expectedValue} and no other thread is also - * attempting to set the variable will eventually succeed. (Such - * spurious failures may for example be due to memory contention effects - * that are unrelated to whether the expected and current values are - * equal.) Additionally {@code weakCompareAndSet} does not provide - * ordering guarantees that are usually needed for synchronization - * control. However, the method may be useful for updating counters and - * statistics when such updates are unrelated to the other - * happens-before orderings of a program. When a thread sees an update - * to an atomic variable caused by a {@code weakCompareAndSet}, it does - * not necessarily see updates to any other variables that - * occurred before the {@code weakCompareAndSet}. This may be - * acceptable when, for example, updating performance statistics, but - * rarely otherwise. - * *

The {@link java.util.concurrent.atomic.AtomicMarkableReference} * class associates a single boolean with a reference. For example, this * bit might be used inside a data structure to mean that the object @@ -185,29 +118,6 @@ * used for example, to represent version numbers corresponding to * series of updates. * - *

Atomic classes are designed primarily as building blocks for - * implementing non-blocking data structures and related infrastructure - * classes. The {@code compareAndSet} method is not a general - * replacement for locking. It applies only when critical updates for an - * object are confined to a single variable. - * - *

Atomic classes are not general purpose replacements for - * {@code java.lang.Integer} and related classes. They do not - * define methods such as {@code equals}, {@code hashCode} and - * {@code compareTo}. (Because atomic variables are expected to be - * mutated, they are poor choices for hash table keys.) Additionally, - * classes are provided only for those types that are commonly useful in - * intended applications. For example, there is no atomic class for - * representing {@code byte}. In those infrequent cases where you would - * like to do so, you can use an {@code AtomicInteger} to hold - * {@code byte} values, and cast appropriately. - * - * You can also hold floats using - * {@link java.lang.Float#floatToRawIntBits} and - * {@link java.lang.Float#intBitsToFloat} conversions, and doubles using - * {@link java.lang.Double#doubleToRawLongBits} and - * {@link java.lang.Double#longBitsToDouble} conversions. - * * @since 1.5 */ package java.util.concurrent.atomic; diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java index cffab6b1286..7c42896079d 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java @@ -35,6 +35,8 @@ package java.util.concurrent.locks; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.ArrayList; import java.util.Collection; import java.util.Date; @@ -113,7 +115,7 @@ public abstract class AbstractQueuedLongSynchronizer protected final void setState(long newState) { // Use putLongVolatile instead of ordinary volatile store when // using compareAndSwapLong, for sake of some 32bit systems. - U.putLongVolatile(this, STATE, newState); + STATE.setVolatile(this, newState); } /** @@ -128,7 +130,7 @@ public abstract class AbstractQueuedLongSynchronizer * value was not equal to the expected value. */ protected final boolean compareAndSetState(long expect, long update) { - return U.compareAndSwapLong(this, STATE, expect, update); + return STATE.compareAndSet(this, expect, update); } // Queuing utilities @@ -149,7 +151,7 @@ public abstract class AbstractQueuedLongSynchronizer for (;;) { Node oldTail = tail; if (oldTail != null) { - U.putObject(node, Node.PREV, oldTail); + node.setPrevRelaxed(oldTail); if (compareAndSetTail(oldTail, node)) { oldTail.next = node; return oldTail; @@ -172,7 +174,7 @@ public abstract class AbstractQueuedLongSynchronizer for (;;) { Node oldTail = tail; if (oldTail != null) { - U.putObject(node, Node.PREV, oldTail); + node.setPrevRelaxed(oldTail); if (compareAndSetTail(oldTail, node)) { oldTail.next = node; return node; @@ -1810,28 +1812,17 @@ public abstract class AbstractQueuedLongSynchronizer } } - /** - * Setup to support compareAndSet. We need to natively implement - * this here: For the sake of permitting future enhancements, we - * cannot explicitly subclass AtomicLong, which would be - * efficient and useful otherwise. So, as the lesser of evils, we - * natively implement using hotspot intrinsics API. And while we - * are at it, we do the same for other CASable fields (which could - * otherwise be done with atomic field updaters). - */ - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long STATE; - private static final long HEAD; - private static final long TAIL; + // VarHandle mechanics + private static final VarHandle STATE; + private static final VarHandle HEAD; + private static final VarHandle TAIL; static { try { - STATE = U.objectFieldOffset - (AbstractQueuedLongSynchronizer.class.getDeclaredField("state")); - HEAD = U.objectFieldOffset - (AbstractQueuedLongSynchronizer.class.getDeclaredField("head")); - TAIL = U.objectFieldOffset - (AbstractQueuedLongSynchronizer.class.getDeclaredField("tail")); + MethodHandles.Lookup l = MethodHandles.lookup(); + STATE = l.findVarHandle(AbstractQueuedLongSynchronizer.class, "state", long.class); + HEAD = l.findVarHandle(AbstractQueuedLongSynchronizer.class, "head", Node.class); + TAIL = l.findVarHandle(AbstractQueuedLongSynchronizer.class, "tail", Node.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -1846,7 +1837,7 @@ public abstract class AbstractQueuedLongSynchronizer */ private final void initializeSyncQueue() { Node h; - if (U.compareAndSwapObject(this, HEAD, null, (h = new Node()))) + if (HEAD.compareAndSet(this, null, (h = new Node()))) tail = h; } @@ -1854,6 +1845,6 @@ public abstract class AbstractQueuedLongSynchronizer * CASes tail field. */ private final boolean compareAndSetTail(Node expect, Node update) { - return U.compareAndSwapObject(this, TAIL, expect, update); + return TAIL.compareAndSet(this, expect, update); } } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java index a90e4fdd629..ba4e9edc942 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java @@ -35,11 +35,12 @@ package java.util.concurrent.locks; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.concurrent.TimeUnit; -import jdk.internal.vm.annotation.ReservedStackAccess; /** * Provides a framework for implementing blocking locks and related @@ -506,40 +507,41 @@ public abstract class AbstractQueuedSynchronizer /** Constructor used by addWaiter. */ Node(Node nextWaiter) { this.nextWaiter = nextWaiter; - U.putObject(this, THREAD, Thread.currentThread()); + THREAD.set(this, Thread.currentThread()); } /** Constructor used by addConditionWaiter. */ Node(int waitStatus) { - U.putInt(this, WAITSTATUS, waitStatus); - U.putObject(this, THREAD, Thread.currentThread()); + WAITSTATUS.set(this, waitStatus); + THREAD.set(this, Thread.currentThread()); } /** CASes waitStatus field. */ final boolean compareAndSetWaitStatus(int expect, int update) { - return U.compareAndSwapInt(this, WAITSTATUS, expect, update); + return WAITSTATUS.compareAndSet(this, expect, update); } /** CASes next field. */ final boolean compareAndSetNext(Node expect, Node update) { - return U.compareAndSwapObject(this, NEXT, expect, update); + return NEXT.compareAndSet(this, expect, update); } - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long NEXT; - static final long PREV; - private static final long THREAD; - private static final long WAITSTATUS; + final void setPrevRelaxed(Node p) { + PREV.set(this, p); + } + + // VarHandle mechanics + private static final VarHandle NEXT; + private static final VarHandle PREV; + private static final VarHandle THREAD; + private static final VarHandle WAITSTATUS; static { try { - NEXT = U.objectFieldOffset - (Node.class.getDeclaredField("next")); - PREV = U.objectFieldOffset - (Node.class.getDeclaredField("prev")); - THREAD = U.objectFieldOffset - (Node.class.getDeclaredField("thread")); - WAITSTATUS = U.objectFieldOffset - (Node.class.getDeclaredField("waitStatus")); + MethodHandles.Lookup l = MethodHandles.lookup(); + NEXT = l.findVarHandle(Node.class, "next", Node.class); + PREV = l.findVarHandle(Node.class, "prev", Node.class); + THREAD = l.findVarHandle(Node.class, "thread", Thread.class); + WAITSTATUS = l.findVarHandle(Node.class, "waitStatus", int.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -595,7 +597,7 @@ public abstract class AbstractQueuedSynchronizer * value was not equal to the expected value. */ protected final boolean compareAndSetState(int expect, int update) { - return U.compareAndSwapInt(this, STATE, expect, update); + return STATE.compareAndSet(this, expect, update); } // Queuing utilities @@ -616,7 +618,7 @@ public abstract class AbstractQueuedSynchronizer for (;;) { Node oldTail = tail; if (oldTail != null) { - U.putObject(node, Node.PREV, oldTail); + node.setPrevRelaxed(oldTail); if (compareAndSetTail(oldTail, node)) { oldTail.next = node; return oldTail; @@ -639,7 +641,7 @@ public abstract class AbstractQueuedSynchronizer for (;;) { Node oldTail = tail; if (oldTail != null) { - U.putObject(node, Node.PREV, oldTail); + node.setPrevRelaxed(oldTail); if (compareAndSetTail(oldTail, node)) { oldTail.next = node; return node; @@ -887,7 +889,6 @@ public abstract class AbstractQueuedSynchronizer * @param arg the acquire argument * @return {@code true} if interrupted while waiting */ - @ReservedStackAccess final boolean acquireQueued(final Node node, int arg) { try { boolean interrupted = false; @@ -1220,7 +1221,6 @@ public abstract class AbstractQueuedSynchronizer * {@link #tryAcquire} but is otherwise uninterpreted and * can represent anything you like. */ - @ReservedStackAccess public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) @@ -1284,7 +1284,6 @@ public abstract class AbstractQueuedSynchronizer * can represent anything you like. * @return the value returned from {@link #tryRelease} */ - @ReservedStackAccess public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; @@ -1365,7 +1364,6 @@ public abstract class AbstractQueuedSynchronizer * and can represent anything you like. * @return the value returned from {@link #tryReleaseShared} */ - @ReservedStackAccess public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); @@ -2279,28 +2277,17 @@ public abstract class AbstractQueuedSynchronizer } } - /** - * Setup to support compareAndSet. We need to natively implement - * this here: For the sake of permitting future enhancements, we - * cannot explicitly subclass AtomicInteger, which would be - * efficient and useful otherwise. So, as the lesser of evils, we - * natively implement using hotspot intrinsics API. And while we - * are at it, we do the same for other CASable fields (which could - * otherwise be done with atomic field updaters). - */ - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long STATE; - private static final long HEAD; - private static final long TAIL; + // VarHandle mechanics + private static final VarHandle STATE; + private static final VarHandle HEAD; + private static final VarHandle TAIL; static { try { - STATE = U.objectFieldOffset - (AbstractQueuedSynchronizer.class.getDeclaredField("state")); - HEAD = U.objectFieldOffset - (AbstractQueuedSynchronizer.class.getDeclaredField("head")); - TAIL = U.objectFieldOffset - (AbstractQueuedSynchronizer.class.getDeclaredField("tail")); + MethodHandles.Lookup l = MethodHandles.lookup(); + STATE = l.findVarHandle(AbstractQueuedSynchronizer.class, "state", int.class); + HEAD = l.findVarHandle(AbstractQueuedSynchronizer.class, "head", Node.class); + TAIL = l.findVarHandle(AbstractQueuedSynchronizer.class, "tail", Node.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -2315,7 +2302,7 @@ public abstract class AbstractQueuedSynchronizer */ private final void initializeSyncQueue() { Node h; - if (U.compareAndSwapObject(this, HEAD, null, (h = new Node()))) + if (HEAD.compareAndSet(this, null, (h = new Node()))) tail = h; } @@ -2323,6 +2310,6 @@ public abstract class AbstractQueuedSynchronizer * CASes tail field. */ private final boolean compareAndSetTail(Node expect, Node update) { - return U.compareAndSwapObject(this, TAIL, expect, update); + return TAIL.compareAndSet(this, expect, update); } } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java index 79181fd9dc6..a4770a5e0ed 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java @@ -396,7 +396,6 @@ public interface Condition { * re-acquire the lock associated with this condition. When the * thread returns it is guaranteed to hold this lock. * - * *

If the current thread: *

    *
  • has its interrupted status set on entry to this method; or @@ -408,7 +407,6 @@ public interface Condition { * case, whether or not the test for interruption occurs before the lock * is released. * - * *

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

     {@code
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
    index c219ca55c7b..7bf6e8a54cd 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
    @@ -35,6 +35,8 @@
     
     package java.util.concurrent.locks;
     
    +import jdk.internal.misc.Unsafe;
    +
     /**
      * Basic thread blocking primitives for creating locks and other
      * synchronization classes.
    @@ -405,16 +407,30 @@ public class LockSupport {
             return r;
         }
     
    +    /**
    +     * Returns the thread id for the given thread.  We must access
    +     * this directly rather than via method Thread.getId() because
    +     * getId() is not final, and has been known to be overridden in
    +     * ways that do not preserve unique mappings.
    +     */
    +    static final long getThreadId(Thread thread) {
    +        return U.getLongVolatile(thread, TID);
    +    }
    +
         // Hotspot implementation via intrinsics API
    -    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
    +    private static final Unsafe U = Unsafe.getUnsafe();
         private static final long PARKBLOCKER;
         private static final long SECONDARY;
    +    private static final long TID;
         static {
             try {
                 PARKBLOCKER = U.objectFieldOffset
                     (Thread.class.getDeclaredField("parkBlocker"));
                 SECONDARY = U.objectFieldOffset
                     (Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
    +            TID = U.objectFieldOffset
    +                (Thread.class.getDeclaredField("tid"));
    +
             } catch (ReflectiveOperationException e) {
                 throw new Error(e);
             }
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java
    index 00fb84530b9..8455944fe4c 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java
    @@ -79,7 +79,6 @@ package java.util.concurrent.locks;
      * and measurement will establish whether the use of a read-write lock is
      * suitable for your application.
      *
    - *
      * 

    Although the basic operation of a read-write lock is straight-forward, * there are many policy decisions that an implementation must make, which * may affect the effectiveness of the read-write lock in a given application. diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java index c39c94724e7..8e8a94f89bd 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java @@ -118,12 +118,6 @@ public class ReentrantLock implements Lock, java.io.Serializable { abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; - /** - * Performs {@link Lock#lock}. The main reason for subclassing - * is to allow fast path for nonfair version. - */ - abstract void lock(); - /** * Performs non-fair tryLock. tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. @@ -201,19 +195,6 @@ public class ReentrantLock implements Lock, java.io.Serializable { */ static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; - - /** - * Performs lock. Try immediate barge, backing up to normal - * acquire on failure. - */ - @ReservedStackAccess - final void lock() { - if (compareAndSetState(0, 1)) - setExclusiveOwnerThread(Thread.currentThread()); - else - acquire(1); - } - protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } @@ -224,11 +205,6 @@ public class ReentrantLock implements Lock, java.io.Serializable { */ static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; - - final void lock() { - acquire(1); - } - /** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. @@ -288,7 +264,7 @@ public class ReentrantLock implements Lock, java.io.Serializable { * at which time the lock hold count is set to one. */ public void lock() { - sync.lock(); + sync.acquire(1); } /** diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java index 3f39da71270..5a9d8bc2980 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java @@ -37,6 +37,7 @@ package java.util.concurrent.locks; import java.util.Collection; import java.util.concurrent.TimeUnit; +import jdk.internal.vm.annotation.ReservedStackAccess; /** * An implementation of {@link ReadWriteLock} supporting similar @@ -278,7 +279,7 @@ public class ReentrantReadWriteLock static final class HoldCounter { int count; // initially 0 // Use id, not reference, to avoid garbage retention - final long tid = getThreadId(Thread.currentThread()); + final long tid = LockSupport.getThreadId(Thread.currentThread()); } /** @@ -367,7 +368,7 @@ public class ReentrantReadWriteLock * both read and write holds that are all released during a * condition wait and re-established in tryAcquire. */ - + @ReservedStackAccess protected final boolean tryRelease(int releases) { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); @@ -379,6 +380,7 @@ public class ReentrantReadWriteLock return free; } + @ReservedStackAccess protected final boolean tryAcquire(int acquires) { /* * Walkthrough: @@ -411,6 +413,7 @@ public class ReentrantReadWriteLock return true; } + @ReservedStackAccess protected final boolean tryReleaseShared(int unused) { Thread current = Thread.currentThread(); if (firstReader == current) { @@ -421,7 +424,8 @@ public class ReentrantReadWriteLock firstReaderHoldCount--; } else { HoldCounter rh = cachedHoldCounter; - if (rh == null || rh.tid != getThreadId(current)) + if (rh == null || + rh.tid != LockSupport.getThreadId(current)) rh = readHolds.get(); int count = rh.count; if (count <= 1) { @@ -447,6 +451,7 @@ public class ReentrantReadWriteLock "attempt to unlock read lock, not locked by current thread"); } + @ReservedStackAccess protected final int tryAcquireShared(int unused) { /* * Walkthrough: @@ -479,7 +484,8 @@ public class ReentrantReadWriteLock firstReaderHoldCount++; } else { HoldCounter rh = cachedHoldCounter; - if (rh == null || rh.tid != getThreadId(current)) + if (rh == null || + rh.tid != LockSupport.getThreadId(current)) cachedHoldCounter = rh = readHolds.get(); else if (rh.count == 0) readHolds.set(rh); @@ -516,7 +522,8 @@ public class ReentrantReadWriteLock } else { if (rh == null) { rh = cachedHoldCounter; - if (rh == null || rh.tid != getThreadId(current)) { + if (rh == null || + rh.tid != LockSupport.getThreadId(current)) { rh = readHolds.get(); if (rh.count == 0) readHolds.remove(); @@ -537,7 +544,8 @@ public class ReentrantReadWriteLock } else { if (rh == null) rh = cachedHoldCounter; - if (rh == null || rh.tid != getThreadId(current)) + if (rh == null || + rh.tid != LockSupport.getThreadId(current)) rh = readHolds.get(); else if (rh.count == 0) readHolds.set(rh); @@ -554,6 +562,7 @@ public class ReentrantReadWriteLock * This is identical in effect to tryAcquire except for lack * of calls to writerShouldBlock. */ + @ReservedStackAccess final boolean tryWriteLock() { Thread current = Thread.currentThread(); int c = getState(); @@ -575,6 +584,7 @@ public class ReentrantReadWriteLock * This is identical in effect to tryAcquireShared except for * lack of calls to readerShouldBlock. */ + @ReservedStackAccess final boolean tryReadLock() { Thread current = Thread.currentThread(); for (;;) { @@ -593,7 +603,8 @@ public class ReentrantReadWriteLock firstReaderHoldCount++; } else { HoldCounter rh = cachedHoldCounter; - if (rh == null || rh.tid != getThreadId(current)) + if (rh == null || + rh.tid != LockSupport.getThreadId(current)) cachedHoldCounter = rh = readHolds.get(); else if (rh.count == 0) readHolds.set(rh); @@ -644,7 +655,7 @@ public class ReentrantReadWriteLock return firstReaderHoldCount; HoldCounter rh = cachedHoldCounter; - if (rh != null && rh.tid == getThreadId(current)) + if (rh != null && rh.tid == LockSupport.getThreadId(current)) return rh.count; int count = readHolds.get().count; @@ -1490,26 +1501,4 @@ public class ReentrantReadWriteLock "[Write locks = " + w + ", Read locks = " + r + "]"; } - /** - * Returns the thread id for the given thread. We must access - * this directly rather than via method Thread.getId() because - * getId() is not final, and has been known to be overridden in - * ways that do not preserve unique mappings. - */ - static final long getThreadId(Thread thread) { - return U.getLongVolatile(thread, TID); - } - - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long TID; - static { - try { - TID = U.objectFieldOffset - (Thread.class.getDeclaredField("tid")); - } catch (ReflectiveOperationException e) { - throw new Error(e); - } - } - } diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/package-info.java b/jdk/src/java.base/share/classes/java/util/concurrent/package-info.java index 387068da401..46b9398f6e2 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/package-info.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/package-info.java @@ -262,7 +262,6 @@ * *

* - * * The methods of all classes in {@code java.util.concurrent} and its * subpackages extend these guarantees to higher-level * synchronization. In particular: diff --git a/jdk/test/java/util/concurrent/tck/Atomic8Test.java b/jdk/test/java/util/concurrent/tck/Atomic8Test.java index 1e49299c21a..721f502cdc1 100644 --- a/jdk/test/java/util/concurrent/tck/Atomic8Test.java +++ b/jdk/test/java/util/concurrent/tck/Atomic8Test.java @@ -179,7 +179,7 @@ public class Atomic8Test extends JSR166TestCase { * result of supplied function */ public void testReferenceGetAndUpdate() { - AtomicReference a = new AtomicReference(one); + AtomicReference a = new AtomicReference<>(one); assertEquals(new Integer(1), a.getAndUpdate(Atomic8Test::addInteger17)); assertEquals(new Integer(18), a.getAndUpdate(Atomic8Test::addInteger17)); assertEquals(new Integer(35), a.get()); @@ -190,7 +190,7 @@ public class Atomic8Test extends JSR166TestCase { * returns result. */ public void testReferenceUpdateAndGet() { - AtomicReference a = new AtomicReference(one); + AtomicReference a = new AtomicReference<>(one); assertEquals(new Integer(18), a.updateAndGet(Atomic8Test::addInteger17)); assertEquals(new Integer(35), a.updateAndGet(Atomic8Test::addInteger17)); assertEquals(new Integer(35), a.get()); @@ -201,7 +201,7 @@ public class Atomic8Test extends JSR166TestCase { * with supplied function. */ public void testReferenceGetAndAccumulate() { - AtomicReference a = new AtomicReference(one); + AtomicReference a = new AtomicReference<>(one); assertEquals(new Integer(1), a.getAndAccumulate(2, Atomic8Test::sumInteger)); assertEquals(new Integer(3), a.getAndAccumulate(3, Atomic8Test::sumInteger)); assertEquals(new Integer(6), a.get()); @@ -212,7 +212,7 @@ public class Atomic8Test extends JSR166TestCase { * returns result. */ public void testReferenceAccumulateAndGet() { - AtomicReference a = new AtomicReference(one); + AtomicReference a = new AtomicReference<>(one); assertEquals(new Integer(7), a.accumulateAndGet(6, Atomic8Test::sumInteger)); assertEquals(new Integer(10), a.accumulateAndGet(3, Atomic8Test::sumInteger)); assertEquals(new Integer(10), a.get()); diff --git a/jdk/test/java/util/concurrent/tck/AtomicBoolean9Test.java b/jdk/test/java/util/concurrent/tck/AtomicBoolean9Test.java new file mode 100644 index 00000000000..7211dca7ebd --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicBoolean9Test.java @@ -0,0 +1,203 @@ +/* + * 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/publicdomain/zero/1.0/ + */ + +import java.util.concurrent.atomic.AtomicBoolean; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicBoolean9Test extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicBoolean9Test.class); + } + + /** + * getPlain returns the last value set + */ + public void testGetPlainSet() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.getPlain()); + ai.set(false); + assertEquals(false, ai.getPlain()); + ai.set(true); + assertEquals(true, ai.getPlain()); + } + + /** + * getOpaque returns the last value set + */ + public void testGetOpaqueSet() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.getOpaque()); + ai.set(false); + assertEquals(false, ai.getOpaque()); + ai.set(true); + assertEquals(true, ai.getOpaque()); + } + + /** + * getAcquire returns the last value set + */ + public void testGetAcquireSet() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.getAcquire()); + ai.set(false); + assertEquals(false, ai.getAcquire()); + ai.set(true); + assertEquals(true, ai.getAcquire()); + } + + /** + * get returns the last value setPlain + */ + public void testGetSetPlain() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.get()); + ai.setPlain(false); + assertEquals(false, ai.get()); + ai.setPlain(true); + assertEquals(true, ai.get()); + } + + /** + * get returns the last value setOpaque + */ + public void testGetSetOpaque() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.get()); + ai.setOpaque(false); + assertEquals(false, ai.get()); + ai.setOpaque(true); + assertEquals(true, ai.get()); + } + + /** + * get returns the last value setRelease + */ + public void testGetSetRelease() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.get()); + ai.setRelease(false); + assertEquals(false, ai.get()); + ai.setRelease(true); + assertEquals(true, ai.get()); + } + + /** + * compareAndExchange succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchange() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.compareAndExchange(true, false)); + assertEquals(false, ai.compareAndExchange(false, false)); + assertEquals(false, ai.get()); + assertEquals(false, ai.compareAndExchange(true, true)); + assertEquals(false, ai.get()); + assertEquals(false, ai.compareAndExchange(false, true)); + assertEquals(true, ai.get()); + } + + /** + * compareAndExchangeAcquire succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeAcquire() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.compareAndExchangeAcquire(true, false)); + assertEquals(false, ai.compareAndExchangeAcquire(false, false)); + assertEquals(false, ai.get()); + assertEquals(false, ai.compareAndExchangeAcquire(true, true)); + assertEquals(false, ai.get()); + assertEquals(false, ai.compareAndExchangeAcquire(false, true)); + assertEquals(true, ai.get()); + } + + /** + * compareAndExchangeRelease succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeRelease() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.compareAndExchangeRelease(true, false)); + assertEquals(false, ai.compareAndExchangeRelease(false, false)); + assertEquals(false, ai.get()); + assertEquals(false, ai.compareAndExchangeRelease(true, true)); + assertEquals(false, ai.get()); + assertEquals(false, ai.compareAndExchangeRelease(false, true)); + assertEquals(true, ai.get()); + } + + /** + * repeated weakCompareAndSetVolatile succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetVolatile() { + AtomicBoolean ai = new AtomicBoolean(true); + do {} while (!ai.weakCompareAndSetVolatile(true, false)); + do {} while (!ai.weakCompareAndSetVolatile(false, false)); + assertEquals(false, ai.get()); + do {} while (!ai.weakCompareAndSetVolatile(false, true)); + assertEquals(true, ai.get()); + } + + /** + * repeated weakCompareAndSetAcquire succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetAcquire() { + AtomicBoolean ai = new AtomicBoolean(true); + do {} while (!ai.weakCompareAndSetAcquire(true, false)); + do {} while (!ai.weakCompareAndSetAcquire(false, false)); + assertEquals(false, ai.get()); + do {} while (!ai.weakCompareAndSetAcquire(false, true)); + assertEquals(true, ai.get()); + } + + /** + * repeated weakCompareAndSetRelease succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetRelease() { + AtomicBoolean ai = new AtomicBoolean(true); + do {} while (!ai.weakCompareAndSetRelease(true, false)); + do {} while (!ai.weakCompareAndSetRelease(false, false)); + assertEquals(false, ai.get()); + do {} while (!ai.weakCompareAndSetRelease(false, true)); + assertEquals(true, ai.get()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicInteger9Test.java b/jdk/test/java/util/concurrent/tck/AtomicInteger9Test.java new file mode 100644 index 00000000000..7a0ed578f9a --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicInteger9Test.java @@ -0,0 +1,203 @@ +/* + * 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/publicdomain/zero/1.0/ + */ + +import java.util.concurrent.atomic.AtomicInteger; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicInteger9Test extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicInteger9Test.class); + } + + /** + * getPlain returns the last value set + */ + public void testGetPlainSet() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.getPlain()); + ai.set(2); + assertEquals(2, ai.getPlain()); + ai.set(-3); + assertEquals(-3, ai.getPlain()); + } + + /** + * getOpaque returns the last value set + */ + public void testGetOpaqueSet() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.getOpaque()); + ai.set(2); + assertEquals(2, ai.getOpaque()); + ai.set(-3); + assertEquals(-3, ai.getOpaque()); + } + + /** + * getAcquire returns the last value set + */ + public void testGetAcquireSet() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.getAcquire()); + ai.set(2); + assertEquals(2, ai.getAcquire()); + ai.set(-3); + assertEquals(-3, ai.getAcquire()); + } + + /** + * get returns the last value setPlain + */ + public void testGetSetPlain() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.get()); + ai.setPlain(2); + assertEquals(2, ai.get()); + ai.setPlain(-3); + assertEquals(-3, ai.get()); + } + + /** + * get returns the last value setOpaque + */ + public void testGetSetOpaque() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.get()); + ai.setOpaque(2); + assertEquals(2, ai.get()); + ai.setOpaque(-3); + assertEquals(-3, ai.get()); + } + + /** + * get returns the last value setRelease + */ + public void testGetSetRelease() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.get()); + ai.setRelease(2); + assertEquals(2, ai.get()); + ai.setRelease(-3); + assertEquals(-3, ai.get()); + } + + /** + * compareAndExchange succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchange() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.compareAndExchange(1, 2)); + assertEquals(2, ai.compareAndExchange(2, -4)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchange(-5, 7)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchange(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * compareAndExchangeAcquire succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeAcquire() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.compareAndExchangeAcquire(1, 2)); + assertEquals(2, ai.compareAndExchangeAcquire(2, -4)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchangeAcquire(-5, 7)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchangeAcquire(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * compareAndExchangeRelease succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeRelease() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.compareAndExchangeRelease(1, 2)); + assertEquals(2, ai.compareAndExchangeRelease(2, -4)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchangeRelease(-5, 7)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchangeRelease(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * repeated weakCompareAndSetVolatile succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetVolatile() { + AtomicInteger ai = new AtomicInteger(1); + do {} while (!ai.weakCompareAndSetVolatile(1, 2)); + do {} while (!ai.weakCompareAndSetVolatile(2, -4)); + assertEquals(-4, ai.get()); + do {} while (!ai.weakCompareAndSetVolatile(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * repeated weakCompareAndSetAcquire succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetAcquire() { + AtomicInteger ai = new AtomicInteger(1); + do {} while (!ai.weakCompareAndSetAcquire(1, 2)); + do {} while (!ai.weakCompareAndSetAcquire(2, -4)); + assertEquals(-4, ai.get()); + do {} while (!ai.weakCompareAndSetAcquire(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * repeated weakCompareAndSetRelease succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetRelease() { + AtomicInteger ai = new AtomicInteger(1); + do {} while (!ai.weakCompareAndSetRelease(1, 2)); + do {} while (!ai.weakCompareAndSetRelease(2, -4)); + assertEquals(-4, ai.get()); + do {} while (!ai.weakCompareAndSetRelease(-4, 7)); + assertEquals(7, ai.get()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicIntegerArray9Test.java b/jdk/test/java/util/concurrent/tck/AtomicIntegerArray9Test.java new file mode 100644 index 00000000000..d074b140ba2 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicIntegerArray9Test.java @@ -0,0 +1,267 @@ +/* + * 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/publicdomain/zero/1.0/ + */ + +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicIntegerArray; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicIntegerArray9Test extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicIntegerArray9Test.class); + } + + /** + * get and set for out of bound indices throw IndexOutOfBoundsException + */ + public void testIndexing() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int index : new int[] { -1, SIZE }) { + final int j = index; + final Runnable[] tasks = { + () -> aa.getPlain(j), + () -> aa.getOpaque(j), + () -> aa.getAcquire(j), + () -> aa.setPlain(j, 1), + () -> aa.setOpaque(j, 1), + () -> aa.setRelease(j, 1), + () -> aa.compareAndExchange(j, 1, 2), + () -> aa.compareAndExchangeAcquire(j, 1, 2), + () -> aa.compareAndExchangeRelease(j, 1, 2), + () -> aa.weakCompareAndSetVolatile(j, 1, 2), + () -> aa.weakCompareAndSetAcquire(j, 1, 2), + () -> aa.weakCompareAndSetRelease(j, 1, 2), + }; + + assertThrows(IndexOutOfBoundsException.class, tasks); + } + } + + /** + * getPlain returns the last value set + */ + public void testGetPlainSet() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getPlain(i)); + aa.set(i, 2); + assertEquals(2, aa.getPlain(i)); + aa.set(i, -3); + assertEquals(-3, aa.getPlain(i)); + } + } + + /** + * getOpaque returns the last value set + */ + public void testGetOpaqueSet() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getOpaque(i)); + aa.set(i, 2); + assertEquals(2, aa.getOpaque(i)); + aa.set(i, -3); + assertEquals(-3, aa.getOpaque(i)); + } + } + + /** + * getAcquire returns the last value set + */ + public void testGetAcquireSet() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getAcquire(i)); + aa.set(i, 2); + assertEquals(2, aa.getAcquire(i)); + aa.set(i, -3); + assertEquals(-3, aa.getAcquire(i)); + } + } + + /** + * get returns the last value setPlain + */ + public void testGetSetPlain() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setPlain(i, 1); + assertEquals(1, aa.get(i)); + aa.setPlain(i, 2); + assertEquals(2, aa.get(i)); + aa.setPlain(i, -3); + assertEquals(-3, aa.get(i)); + } + } + + /** + * get returns the last value setOpaque + */ + public void testGetSetOpaque() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setOpaque(i, 1); + assertEquals(1, aa.get(i)); + aa.setOpaque(i, 2); + assertEquals(2, aa.get(i)); + aa.setOpaque(i, -3); + assertEquals(-3, aa.get(i)); + } + } + + /** + * get returns the last value setRelease + */ + public void testGetSetRelease() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setRelease(i, 1); + assertEquals(1, aa.get(i)); + aa.setRelease(i, 2); + assertEquals(2, aa.get(i)); + aa.setRelease(i, -3); + assertEquals(-3, aa.get(i)); + } + } + + /** + * compareAndExchange succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchange() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.compareAndExchange(i, 1, 2)); + assertEquals(2, aa.compareAndExchange(i, 2, -4)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchange(i,-5, 7)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchange(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * compareAndExchangeAcquire succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeAcquire() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.compareAndExchangeAcquire(i, 1, 2)); + assertEquals(2, aa.compareAndExchangeAcquire(i, 2, -4)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchangeAcquire(i,-5, 7)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchangeAcquire(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * compareAndExchangeRelease succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeRelease() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.compareAndExchangeRelease(i, 1, 2)); + assertEquals(2, aa.compareAndExchangeRelease(i, 2, -4)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchangeRelease(i,-5, 7)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchangeRelease(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetVolatile succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetVolatile() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + do {} while (!aa.weakCompareAndSetVolatile(i, 1, 2)); + do {} while (!aa.weakCompareAndSetVolatile(i, 2, -4)); + assertEquals(-4, aa.get(i)); + do {} while (!aa.weakCompareAndSetVolatile(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetAcquire succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetAcquire() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + do {} while (!aa.weakCompareAndSetAcquire(i, 1, 2)); + do {} while (!aa.weakCompareAndSetAcquire(i, 2, -4)); + assertEquals(-4, aa.get(i)); + do {} while (!aa.weakCompareAndSetAcquire(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetRelease succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetRelease() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + do {} while (!aa.weakCompareAndSetRelease(i, 1, 2)); + do {} while (!aa.weakCompareAndSetRelease(i, 2, -4)); + assertEquals(-4, aa.get(i)); + do {} while (!aa.weakCompareAndSetRelease(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicLong9Test.java b/jdk/test/java/util/concurrent/tck/AtomicLong9Test.java new file mode 100644 index 00000000000..c11561690b8 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicLong9Test.java @@ -0,0 +1,203 @@ +/* + * 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/publicdomain/zero/1.0/ + */ + +import java.util.concurrent.atomic.AtomicLong; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicLong9Test extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicLong9Test.class); + } + + /** + * getPlain returns the last value set + */ + public void testGetPlainSet() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.getPlain()); + ai.set(2); + assertEquals(2, ai.getPlain()); + ai.set(-3); + assertEquals(-3, ai.getPlain()); + } + + /** + * getOpaque returns the last value set + */ + public void testGetOpaqueSet() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.getOpaque()); + ai.set(2); + assertEquals(2, ai.getOpaque()); + ai.set(-3); + assertEquals(-3, ai.getOpaque()); + } + + /** + * getAcquire returns the last value set + */ + public void testGetAcquireSet() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.getAcquire()); + ai.set(2); + assertEquals(2, ai.getAcquire()); + ai.set(-3); + assertEquals(-3, ai.getAcquire()); + } + + /** + * get returns the last value setPlain + */ + public void testGetSetPlain() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.get()); + ai.setPlain(2); + assertEquals(2, ai.get()); + ai.setPlain(-3); + assertEquals(-3, ai.get()); + } + + /** + * get returns the last value setOpaque + */ + public void testGetSetOpaque() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.get()); + ai.setOpaque(2); + assertEquals(2, ai.get()); + ai.setOpaque(-3); + assertEquals(-3, ai.get()); + } + + /** + * get returns the last value setRelease + */ + public void testGetSetRelease() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.get()); + ai.setRelease(2); + assertEquals(2, ai.get()); + ai.setRelease(-3); + assertEquals(-3, ai.get()); + } + + /** + * compareAndExchange succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchange() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.compareAndExchange(1, 2)); + assertEquals(2, ai.compareAndExchange(2, -4)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchange(-5, 7)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchange(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * compareAndExchangeAcquire succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeAcquire() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.compareAndExchangeAcquire(1, 2)); + assertEquals(2, ai.compareAndExchangeAcquire(2, -4)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchangeAcquire(-5, 7)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchangeAcquire(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * compareAndExchangeRelease succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeRelease() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.compareAndExchangeRelease(1, 2)); + assertEquals(2, ai.compareAndExchangeRelease(2, -4)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchangeRelease(-5, 7)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchangeRelease(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * repeated weakCompareAndSetVolatile succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetVolatile() { + AtomicLong ai = new AtomicLong(1); + do {} while (!ai.weakCompareAndSetVolatile(1, 2)); + do {} while (!ai.weakCompareAndSetVolatile(2, -4)); + assertEquals(-4, ai.get()); + do {} while (!ai.weakCompareAndSetVolatile(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * repeated weakCompareAndSetAcquire succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetAcquire() { + AtomicLong ai = new AtomicLong(1); + do {} while (!ai.weakCompareAndSetAcquire(1, 2)); + do {} while (!ai.weakCompareAndSetAcquire(2, -4)); + assertEquals(-4, ai.get()); + do {} while (!ai.weakCompareAndSetAcquire(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * repeated weakCompareAndSetRelease succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetRelease() { + AtomicLong ai = new AtomicLong(1); + do {} while (!ai.weakCompareAndSetRelease(1, 2)); + do {} while (!ai.weakCompareAndSetRelease(2, -4)); + assertEquals(-4, ai.get()); + do {} while (!ai.weakCompareAndSetRelease(-4, 7)); + assertEquals(7, ai.get()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicLongArray9Test.java b/jdk/test/java/util/concurrent/tck/AtomicLongArray9Test.java new file mode 100644 index 00000000000..488b195e014 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicLongArray9Test.java @@ -0,0 +1,266 @@ +/* + * 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/publicdomain/zero/1.0/ + */ + +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicLongArray; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicLongArray9Test extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicLongArray9Test.class); + } + + /** + * get and set for out of bound indices throw IndexOutOfBoundsException + */ + public void testIndexing() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int index : new int[] { -1, SIZE }) { + final int j = index; + final Runnable[] tasks = { + () -> aa.getPlain(j), + () -> aa.getOpaque(j), + () -> aa.getAcquire(j), + () -> aa.setPlain(j, 1), + () -> aa.setOpaque(j, 1), + () -> aa.setRelease(j, 1), + () -> aa.compareAndExchange(j, 1, 2), + () -> aa.compareAndExchangeAcquire(j, 1, 2), + () -> aa.compareAndExchangeRelease(j, 1, 2), + () -> aa.weakCompareAndSetVolatile(j, 1, 2), + () -> aa.weakCompareAndSetAcquire(j, 1, 2), + () -> aa.weakCompareAndSetRelease(j, 1, 2), + }; + + assertThrows(IndexOutOfBoundsException.class, tasks); + } + } + + /** + * getPlain returns the last value set + */ + public void testGetPlainSet() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getPlain(i)); + aa.set(i, 2); + assertEquals(2, aa.getPlain(i)); + aa.set(i, -3); + assertEquals(-3, aa.getPlain(i)); + } + } + + /** + * getOpaque returns the last value set + */ + public void testGetOpaqueSet() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getOpaque(i)); + aa.set(i, 2); + assertEquals(2, aa.getOpaque(i)); + aa.set(i, -3); + assertEquals(-3, aa.getOpaque(i)); + } + } + + /** + * getAcquire returns the last value set + */ + public void testGetAcquireSet() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getAcquire(i)); + aa.set(i, 2); + assertEquals(2, aa.getAcquire(i)); + aa.set(i, -3); + assertEquals(-3, aa.getAcquire(i)); + } + } + + /** + * get returns the last value setPlain + */ + public void testGetSetPlain() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setPlain(i, 1); + assertEquals(1, aa.get(i)); + aa.setPlain(i, 2); + assertEquals(2, aa.get(i)); + aa.setPlain(i, -3); + assertEquals(-3, aa.get(i)); + } + } + + /** + * get returns the last value setOpaque + */ + public void testGetSetOpaque() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setOpaque(i, 1); + assertEquals(1, aa.get(i)); + aa.setOpaque(i, 2); + assertEquals(2, aa.get(i)); + aa.setOpaque(i, -3); + assertEquals(-3, aa.get(i)); + } + } + + /** + * get returns the last value setRelease + */ + public void testGetSetRelease() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setRelease(i, 1); + assertEquals(1, aa.get(i)); + aa.setRelease(i, 2); + assertEquals(2, aa.get(i)); + aa.setRelease(i, -3); + assertEquals(-3, aa.get(i)); + } + } + + /** + * compareAndExchange succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchange() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.compareAndExchange(i, 1, 2)); + assertEquals(2, aa.compareAndExchange(i, 2, -4)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchange(i,-5, 7)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchange(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * compareAndExchangeAcquire succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeAcquire() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.compareAndExchangeAcquire(i, 1, 2)); + assertEquals(2, aa.compareAndExchangeAcquire(i, 2, -4)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchangeAcquire(i,-5, 7)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchangeAcquire(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * compareAndExchangeRelease succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeRelease() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.compareAndExchangeRelease(i, 1, 2)); + assertEquals(2, aa.compareAndExchangeRelease(i, 2, -4)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchangeRelease(i,-5, 7)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchangeRelease(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetVolatile succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetVolatile() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + do {} while (!aa.weakCompareAndSetVolatile(i, 1, 2)); + do {} while (!aa.weakCompareAndSetVolatile(i, 2, -4)); + assertEquals(-4, aa.get(i)); + do {} while (!aa.weakCompareAndSetVolatile(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetAcquire succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetAcquire() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + do {} while (!aa.weakCompareAndSetAcquire(i, 1, 2)); + do {} while (!aa.weakCompareAndSetAcquire(i, 2, -4)); + assertEquals(-4, aa.get(i)); + do {} while (!aa.weakCompareAndSetAcquire(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetRelease succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetRelease() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + do {} while (!aa.weakCompareAndSetRelease(i, 1, 2)); + do {} while (!aa.weakCompareAndSetRelease(i, 2, -4)); + assertEquals(-4, aa.get(i)); + do {} while (!aa.weakCompareAndSetRelease(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicReference9Test.java b/jdk/test/java/util/concurrent/tck/AtomicReference9Test.java new file mode 100644 index 00000000000..f1d974908c7 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicReference9Test.java @@ -0,0 +1,203 @@ +/* + * 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/publicdomain/zero/1.0/ + */ + +import java.util.concurrent.atomic.AtomicReference; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicReference9Test extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicReference9Test.class); + } + + /** + * getPlain returns the last value set + */ + public void testGetPlainSet() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.getPlain()); + ai.set(two); + assertEquals(two, ai.getPlain()); + ai.set(m3); + assertEquals(m3, ai.getPlain()); + } + + /** + * getOpaque returns the last value set + */ + public void testGetOpaqueSet() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.getOpaque()); + ai.set(two); + assertEquals(two, ai.getOpaque()); + ai.set(m3); + assertEquals(m3, ai.getOpaque()); + } + + /** + * getAcquire returns the last value set + */ + public void testGetAcquireSet() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.getAcquire()); + ai.set(two); + assertEquals(two, ai.getAcquire()); + ai.set(m3); + assertEquals(m3, ai.getAcquire()); + } + + /** + * get returns the last value setPlain + */ + public void testGetSetPlain() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.get()); + ai.setPlain(two); + assertEquals(two, ai.get()); + ai.setPlain(m3); + assertEquals(m3, ai.get()); + } + + /** + * get returns the last value setOpaque + */ + public void testGetSetOpaque() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.get()); + ai.setOpaque(two); + assertEquals(two, ai.get()); + ai.setOpaque(m3); + assertEquals(m3, ai.get()); + } + + /** + * get returns the last value setRelease + */ + public void testGetSetRelease() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.get()); + ai.setRelease(two); + assertEquals(two, ai.get()); + ai.setRelease(m3); + assertEquals(m3, ai.get()); + } + + /** + * compareAndExchange succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchange() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.compareAndExchange(one, two)); + assertEquals(two, ai.compareAndExchange(two, m4)); + assertEquals(m4, ai.get()); + assertEquals(m4, ai.compareAndExchange(m5, seven)); + assertEquals(m4, ai.get()); + assertEquals(m4, ai.compareAndExchange(m4, seven)); + assertEquals(seven, ai.get()); + } + + /** + * compareAndExchangeAcquire succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeAcquire() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.compareAndExchangeAcquire(one, two)); + assertEquals(two, ai.compareAndExchangeAcquire(two, m4)); + assertEquals(m4, ai.get()); + assertEquals(m4, ai.compareAndExchangeAcquire(m5, seven)); + assertEquals(m4, ai.get()); + assertEquals(m4, ai.compareAndExchangeAcquire(m4, seven)); + assertEquals(seven, ai.get()); + } + + /** + * compareAndExchangeRelease succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeRelease() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.compareAndExchangeRelease(one, two)); + assertEquals(two, ai.compareAndExchangeRelease(two, m4)); + assertEquals(m4, ai.get()); + assertEquals(m4, ai.compareAndExchangeRelease(m5, seven)); + assertEquals(m4, ai.get()); + assertEquals(m4, ai.compareAndExchangeRelease(m4, seven)); + assertEquals(seven, ai.get()); + } + + /** + * repeated weakCompareAndSetVolatile succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetVolatile() { + AtomicReference ai = new AtomicReference<>(one); + do {} while (!ai.weakCompareAndSetVolatile(one, two)); + do {} while (!ai.weakCompareAndSetVolatile(two, m4)); + assertEquals(m4, ai.get()); + do {} while (!ai.weakCompareAndSetVolatile(m4, seven)); + assertEquals(seven, ai.get()); + } + + /** + * repeated weakCompareAndSetAcquire succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetAcquire() { + AtomicReference ai = new AtomicReference<>(one); + do {} while (!ai.weakCompareAndSetAcquire(one, two)); + do {} while (!ai.weakCompareAndSetAcquire(two, m4)); + assertEquals(m4, ai.get()); + do {} while (!ai.weakCompareAndSetAcquire(m4, seven)); + assertEquals(seven, ai.get()); + } + + /** + * repeated weakCompareAndSetRelease succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetRelease() { + AtomicReference ai = new AtomicReference<>(one); + do {} while (!ai.weakCompareAndSetRelease(one, two)); + do {} while (!ai.weakCompareAndSetRelease(two, m4)); + assertEquals(m4, ai.get()); + do {} while (!ai.weakCompareAndSetRelease(m4, seven)); + assertEquals(seven, ai.get()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicReferenceArray9Test.java b/jdk/test/java/util/concurrent/tck/AtomicReferenceArray9Test.java new file mode 100644 index 00000000000..9ebec5b2ef6 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceArray9Test.java @@ -0,0 +1,266 @@ +/* + * 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/publicdomain/zero/1.0/ + */ + +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicReferenceArray; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicReferenceArray9Test extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicReferenceArray9Test.class); + } + + /** + * get and set for out of bound indices throw IndexOutOfBoundsException + */ + public void testIndexing() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int index : new int[] { -1, SIZE }) { + final int j = index; + final Runnable[] tasks = { + () -> aa.getPlain(j), + () -> aa.getOpaque(j), + () -> aa.getAcquire(j), + () -> aa.setPlain(j, null), + () -> aa.setOpaque(j, null), + () -> aa.setRelease(j, null), + () -> aa.compareAndExchange(j, null, null), + () -> aa.compareAndExchangeAcquire(j, null, null), + () -> aa.compareAndExchangeRelease(j, null, null), + () -> aa.weakCompareAndSetVolatile(j, null, null), + () -> aa.weakCompareAndSetAcquire(j, null, null), + () -> aa.weakCompareAndSetRelease(j, null, null), + }; + + assertThrows(IndexOutOfBoundsException.class, tasks); + } + } + + /** + * getPlain returns the last value set + */ + public void testGetPlainSet() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + assertEquals(one, aa.getPlain(i)); + aa.set(i, two); + assertEquals(two, aa.getPlain(i)); + aa.set(i, m3); + assertEquals(m3, aa.getPlain(i)); + } + } + + /** + * getOpaque returns the last value set + */ + public void testGetOpaqueSet() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + assertEquals(one, aa.getOpaque(i)); + aa.set(i, two); + assertEquals(two, aa.getOpaque(i)); + aa.set(i, m3); + assertEquals(m3, aa.getOpaque(i)); + } + } + + /** + * getAcquire returns the last value set + */ + public void testGetAcquireSet() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + assertEquals(one, aa.getAcquire(i)); + aa.set(i, two); + assertEquals(two, aa.getAcquire(i)); + aa.set(i, m3); + assertEquals(m3, aa.getAcquire(i)); + } + } + + /** + * get returns the last value setPlain + */ + public void testGetSetPlain() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setPlain(i, one); + assertEquals(one, aa.get(i)); + aa.setPlain(i, two); + assertEquals(two, aa.get(i)); + aa.setPlain(i, m3); + assertEquals(m3, aa.get(i)); + } + } + + /** + * get returns the last value setOpaque + */ + public void testGetSetOpaque() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setOpaque(i, one); + assertEquals(one, aa.get(i)); + aa.setOpaque(i, two); + assertEquals(two, aa.get(i)); + aa.setOpaque(i, m3); + assertEquals(m3, aa.get(i)); + } + } + + /** + * get returns the last value setRelease + */ + public void testGetSetRelease() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setRelease(i, one); + assertEquals(one, aa.get(i)); + aa.setRelease(i, two); + assertEquals(two, aa.get(i)); + aa.setRelease(i, m3); + assertEquals(m3, aa.get(i)); + } + } + + /** + * compareAndExchange succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchange() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + assertEquals(one, aa.compareAndExchange(i, one, two)); + assertEquals(two, aa.compareAndExchange(i, two, m4)); + assertEquals(m4, aa.get(i)); + assertEquals(m4, aa.compareAndExchange(i,m5, seven)); + assertEquals(m4, aa.get(i)); + assertEquals(m4, aa.compareAndExchange(i, m4, seven)); + assertEquals(seven, aa.get(i)); + } + } + + /** + * compareAndExchangeAcquire succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeAcquire() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + assertEquals(one, aa.compareAndExchangeAcquire(i, one, two)); + assertEquals(two, aa.compareAndExchangeAcquire(i, two, m4)); + assertEquals(m4, aa.get(i)); + assertEquals(m4, aa.compareAndExchangeAcquire(i,m5, seven)); + assertEquals(m4, aa.get(i)); + assertEquals(m4, aa.compareAndExchangeAcquire(i, m4, seven)); + assertEquals(seven, aa.get(i)); + } + } + + /** + * compareAndExchangeRelease succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeRelease() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + assertEquals(one, aa.compareAndExchangeRelease(i, one, two)); + assertEquals(two, aa.compareAndExchangeRelease(i, two, m4)); + assertEquals(m4, aa.get(i)); + assertEquals(m4, aa.compareAndExchangeRelease(i,m5, seven)); + assertEquals(m4, aa.get(i)); + assertEquals(m4, aa.compareAndExchangeRelease(i, m4, seven)); + assertEquals(seven, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetVolatile succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetVolatile() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + do {} while (!aa.weakCompareAndSetVolatile(i, one, two)); + do {} while (!aa.weakCompareAndSetVolatile(i, two, m4)); + assertEquals(m4, aa.get(i)); + do {} while (!aa.weakCompareAndSetVolatile(i, m4, seven)); + assertEquals(seven, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetAcquire succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetAcquire() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + do {} while (!aa.weakCompareAndSetAcquire(i, one, two)); + do {} while (!aa.weakCompareAndSetAcquire(i, two, m4)); + assertEquals(m4, aa.get(i)); + do {} while (!aa.weakCompareAndSetAcquire(i, m4, seven)); + assertEquals(seven, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetRelease succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetRelease() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + do {} while (!aa.weakCompareAndSetRelease(i, one, two)); + do {} while (!aa.weakCompareAndSetRelease(i, two, m4)); + assertEquals(m4, aa.get(i)); + do {} while (!aa.weakCompareAndSetRelease(i, m4, seven)); + assertEquals(seven, aa.get(i)); + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java b/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java index 2457d40b426..8e7bf10642f 100644 --- a/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java +++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java @@ -243,4 +243,5 @@ public class AtomicReferenceArrayTest extends JSR166TestCase { AtomicReferenceArray aa = new AtomicReferenceArray(a); assertEquals(Arrays.toString(a), aa.toString()); } + } diff --git a/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java b/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java index bae2a43906a..6242374ee89 100644 --- a/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java +++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java @@ -161,7 +161,7 @@ public class AtomicReferenceTest extends JSR166TestCase { * toString returns current value. */ public void testToString() { - AtomicReference ai = new AtomicReference(one); + AtomicReference ai = new AtomicReference<>(one); assertEquals(one.toString(), ai.toString()); ai.set(two); assertEquals(two.toString(), ai.toString()); diff --git a/jdk/test/java/util/concurrent/tck/JSR166TestCase.java b/jdk/test/java/util/concurrent/tck/JSR166TestCase.java index 14907badc6f..8ea9b697bc4 100644 --- a/jdk/test/java/util/concurrent/tck/JSR166TestCase.java +++ b/jdk/test/java/util/concurrent/tck/JSR166TestCase.java @@ -548,6 +548,13 @@ public class JSR166TestCase extends TestCase { // Java9+ test classes if (atLeastJava9()) { String[] java9TestClassNames = { + "AtomicBoolean9Test", + "AtomicInteger9Test", + "AtomicIntegerArray9Test", + "AtomicLong9Test", + "AtomicLongArray9Test", + "AtomicReference9Test", + "AtomicReferenceArray9Test", "ExecutorCompletionService9Test", }; addNamedTestClasses(suite, java9TestClassNames); @@ -975,7 +982,11 @@ public class JSR166TestCase extends TestCase { } } - /** Like Runnable, but with the freedom to throw anything */ + /** + * Like Runnable, but with the freedom to throw anything. + * junit folks had the same idea: + * http://junit.org/junit5/docs/snapshot/api/org/junit/gen5/api/Executable.html + */ interface Action { public void run() throws Throwable; } /** @@ -1006,6 +1017,15 @@ public class JSR166TestCase extends TestCase { * Uninteresting threads are filtered out. */ static void dumpTestThreads() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + System.setSecurityManager(null); + } catch (SecurityException giveUp) { + return; + } + } + ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); System.err.println("------ stacktrace dump start ------"); for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true)) { @@ -1023,6 +1043,8 @@ public class JSR166TestCase extends TestCase { System.err.print(info); } System.err.println("------ stacktrace dump end ------"); + + if (sm != null) System.setSecurityManager(sm); } /**