diff -r d935d42f6d93 -r 9548f8d846e9 jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java Fri Jul 15 13:59:58 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java Fri Jul 15 14:04:09 2016 -0700 @@ -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 @@ } /** - * 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 @@ * @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 @@ * @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 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 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 @@ 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); + } + }