Skip to content

Commit

Permalink
Implemented the following methods for
Browse files Browse the repository at this point in the history
`AtomicDouble` and `AtomicDoubleArray`:

* `accumulateAndGet`
* `getAndAccumulate`
* `updateAndGet`
* `getAndUpdate`

Closes #5784.
Fixes #5742.

RELNOTES=Implement accumulate/update methods for `AtomicDouble` and `AtomicDoubleArray`.
PiperOrigin-RevId: 412110543
  • Loading branch information
allanhaywood authored and Google Java Core Libraries committed Nov 24, 2021
1 parent bdb43e6 commit 2d875d3
Show file tree
Hide file tree
Showing 4 changed files with 365 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@

package com.google.common.util.concurrent;

import static java.lang.Math.max;

import com.google.common.annotations.GwtIncompatible;
import com.google.common.testing.NullPointerTester;
import java.util.Arrays;

/** Unit test for {@link AtomicDoubleArray}. */
Expand Down Expand Up @@ -48,6 +52,13 @@ static void assertBitEquals(double x, double y) {
assertEquals(Double.doubleToRawLongBits(x), Double.doubleToRawLongBits(y));
}

@GwtIncompatible // NullPointerTester
public void testNulls() {
new NullPointerTester().testAllPublicStaticMethods(AtomicDoubleArray.class);
new NullPointerTester().testAllPublicConstructors(AtomicDoubleArray.class);
new NullPointerTester().testAllPublicInstanceMethods(new AtomicDoubleArray(1));
}

/** constructor creates array of given size with all elements zero */
public void testConstructor() {
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
Expand Down Expand Up @@ -211,7 +222,8 @@ public void testWeakCompareAndSet() {
assertBitEquals(prev, aa.get(i));
assertFalse(aa.weakCompareAndSet(i, unused, x));
assertBitEquals(prev, aa.get(i));
while (!aa.weakCompareAndSet(i, prev, x)) {;
while (!aa.weakCompareAndSet(i, prev, x)) {
;
}
assertBitEquals(x, aa.get(i));
prev = x;
Expand Down Expand Up @@ -261,6 +273,128 @@ public void testAddAndGet() {
}
}

/** getAndAccumulate with sum adds given value to current, and returns previous value */
public void testGetAndAccumulateWithSum() {
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
for (int i : new int[] {0, SIZE - 1}) {
for (double x : VALUES) {
for (double y : VALUES) {
aa.set(i, x);
double z = aa.getAndAccumulate(i, y, Double::sum);
assertBitEquals(x, z);
assertBitEquals(x + y, aa.get(i));
}
}
}
}

/** getAndAccumulate with max stores max of given value to current, and returns previous value */
public void testGetAndAccumulateWithMax() {
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
for (int i : new int[] {0, SIZE - 1}) {
for (double x : VALUES) {
for (double y : VALUES) {
aa.set(i, x);
double z = aa.getAndAccumulate(i, y, Double::max);
double expectedMax = max(x, y);
assertBitEquals(x, z);
assertBitEquals(expectedMax, aa.get(i));
}
}
}
}

/** accumulateAndGet with sum adds given value to current, and returns current value */
public void testAccumulateAndGetWithSum() {
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
for (int i : new int[] {0, SIZE - 1}) {
for (double x : VALUES) {
for (double y : VALUES) {
aa.set(i, x);
double z = aa.accumulateAndGet(i, y, Double::sum);
assertBitEquals(x + y, z);
assertBitEquals(x + y, aa.get(i));
}
}
}
}

/** accumulateAndGet with max stores max of given value to current, and returns current value */
public void testAccumulateAndGetWithMax() {
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
for (int i : new int[] {0, SIZE - 1}) {
for (double x : VALUES) {
for (double y : VALUES) {
aa.set(i, x);
double z = aa.accumulateAndGet(i, y, Double::max);
double expectedMax = max(x, y);
assertBitEquals(expectedMax, z);
assertBitEquals(expectedMax, aa.get(i));
}
}
}
}

/** getAndUpdate adds given value to current, and returns previous value */
public void testGetAndUpdateWithSum() {
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
for (int i : new int[] {0, SIZE - 1}) {
for (double x : VALUES) {
for (double y : VALUES) {
aa.set(i, x);
double z = aa.getAndUpdate(i, value -> value + y);
assertBitEquals(x, z);
assertBitEquals(x + y, aa.get(i));
}
}
}
}

/** getAndUpdate subtracts given value to current, and returns previous value */
public void testGetAndUpdateWithSubtract() {
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
for (int i : new int[] {0, SIZE - 1}) {
for (double x : VALUES) {
for (double y : VALUES) {
aa.set(i, x);
double z = aa.getAndUpdate(i, value -> value - y);
assertBitEquals(x, z);
assertBitEquals(x - y, aa.get(i));
}
}
}
}

/** updateAndGet adds given value to current, and returns current value */
public void testUpdateAndGetWithSum() {
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
for (int i : new int[] {0, SIZE - 1}) {
for (double x : VALUES) {
for (double y : VALUES) {
aa.set(i, x);
double z = aa.updateAndGet(i, value -> value + y);
assertBitEquals(x + y, z);
assertBitEquals(x + y, aa.get(i));
}
}
}
}

/** updateAndGet subtracts given value to current, and returns current value */
public void testUpdateAndGetWithSubtract() {
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
for (int i : new int[] {0, SIZE - 1}) {
for (double x : VALUES) {
for (double y : VALUES) {
aa.set(i, x);
double z = aa.updateAndGet(i, value -> value - y);
assertBitEquals(x - y, z);
assertBitEquals(x - y, aa.get(i));
}
}
}
}

static final long COUNTDOWN = 100000;

class Counter extends CheckedRunnable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

package com.google.common.util.concurrent;

import static java.lang.Math.max;


/** Unit test for {@link AtomicDouble}. */
public class AtomicDoubleTest extends JSR166TestCase {
Expand Down Expand Up @@ -125,7 +127,8 @@ public void testWeakCompareAndSet() {
assertBitEquals(prev, at.get());
assertFalse(at.weakCompareAndSet(unused, x));
assertBitEquals(prev, at.get());
while (!at.weakCompareAndSet(prev, x)) {;
while (!at.weakCompareAndSet(prev, x)) {
;
}
assertBitEquals(x, at.get());
prev = x;
Expand Down Expand Up @@ -166,6 +169,108 @@ public void testAddAndGet() {
}
}

/** getAndAccumulate with sum adds given value to current, and returns previous value */
public void testGetAndAccumulateWithSum() {
for (double x : VALUES) {
for (double y : VALUES) {
AtomicDouble a = new AtomicDouble(x);
double z = a.getAndAccumulate(y, Double::sum);
assertBitEquals(x, z);
assertBitEquals(x + y, a.get());
}
}
}

/** getAndAccumulate with max stores max of given value to current, and returns previous value */
public void testGetAndAccumulateWithMax() {
for (double x : VALUES) {
for (double y : VALUES) {
AtomicDouble a = new AtomicDouble(x);
double z = a.getAndAccumulate(y, Double::max);
double expectedMax = max(x, y);
assertBitEquals(x, z);
assertBitEquals(expectedMax, a.get());
}
}
}

/** accumulateAndGet with sum adds given value to current, and returns current value */
public void testAccumulateAndGetWithSum() {
for (double x : VALUES) {
for (double y : VALUES) {
AtomicDouble a = new AtomicDouble(x);
double z = a.accumulateAndGet(y, Double::sum);
assertBitEquals(x + y, z);
assertBitEquals(x + y, a.get());
}
}
}

/** accumulateAndGet with max stores max of given value to current, and returns current value */
public void testAccumulateAndGetWithMax() {
for (double x : VALUES) {
for (double y : VALUES) {
AtomicDouble a = new AtomicDouble(x);
double z = a.accumulateAndGet(y, Double::max);
double expectedMax = max(x, y);
assertBitEquals(expectedMax, z);
assertBitEquals(expectedMax, a.get());
}
}
}

/** getAndUpdate with sum stores sum of given value to current, and returns previous value */
public void testGetAndUpdateWithSum() {
for (double x : VALUES) {
for (double y : VALUES) {
AtomicDouble a = new AtomicDouble(x);
double z = a.getAndUpdate(value -> value + y);
assertBitEquals(x, z);
assertBitEquals(x + y, a.get());
}
}
}

/**
* getAndUpdate with subtract stores subtraction of value from current, and returns previous value
*/
public void testGetAndUpdateWithSubtract() {
for (double x : VALUES) {
for (double y : VALUES) {
AtomicDouble a = new AtomicDouble(x);
double z = a.getAndUpdate(value -> value - y);
assertBitEquals(x, z);
assertBitEquals(x - y, a.get());
}
}
}

/** updateAndGet with sum stores sum of given value to current, and returns current value */
public void testUpdateAndGetWithSum() {
for (double x : VALUES) {
for (double y : VALUES) {
AtomicDouble a = new AtomicDouble(x);
double z = a.updateAndGet(value -> value + y);
assertBitEquals(x + y, z);
assertBitEquals(x + y, a.get());
}
}
}

/**
* updateAndGet with subtract stores subtraction of value from current, and returns current value
*/
public void testUpdateAndGetWithSubtract() {
for (double x : VALUES) {
for (double y : VALUES) {
AtomicDouble a = new AtomicDouble(x);
double z = a.updateAndGet(value -> value - y);
assertBitEquals(x - y, z);
assertBitEquals(x - y, a.get());
}
}
}

/** a deserialized serialized atomic holds same value */
public void testSerialization() throws Exception {
AtomicDouble a = new AtomicDouble();
Expand Down
64 changes: 59 additions & 5 deletions guava/src/com/google/common/util/concurrent/AtomicDouble.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@

package com.google.common.util.concurrent;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.Double.doubleToRawLongBits;
import static java.lang.Double.longBitsToDouble;

import com.google.common.annotations.GwtIncompatible;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.j2objc.annotations.ReflectionSupport;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.function.DoubleBinaryOperator;
import java.util.function.DoubleUnaryOperator;

/**
* A {@code double} value that may be updated atomically. See the {@link
Expand Down Expand Up @@ -155,10 +158,61 @@ public final boolean weakCompareAndSet(double expect, double update) {
*/
@CanIgnoreReturnValue
public final double getAndAdd(double delta) {
return getAndAccumulate(delta, Double::sum);
}

/**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the updated value
*/
@CanIgnoreReturnValue
public final double addAndGet(double delta) {
return accumulateAndGet(delta, Double::sum);
}

/**
* Atomically updates the current value with the results of applying the given function to the
* current and given values.
*
* @param x the update value
* @param accumulatorFunction the accumulator function
* @return the previous value
*/
@CanIgnoreReturnValue
public final double getAndAccumulate(double x, DoubleBinaryOperator accumulatorFunction) {
checkNotNull(accumulatorFunction);
return getAndUpdate(oldValue -> accumulatorFunction.applyAsDouble(oldValue, x));
}

/**
* Atomically updates the current value with the results of applying the given function to the
* current and given values.
*
* @param x the update value
* @param accumulatorFunction the accumulator function
* @return the updated value
* @since NEXT
*/
@CanIgnoreReturnValue
public final double accumulateAndGet(double x, DoubleBinaryOperator accumulatorFunction) {
checkNotNull(accumulatorFunction);
return updateAndGet(oldValue -> accumulatorFunction.applyAsDouble(oldValue, x));
}

/**
* Atomically updates the current value with the results of applying the given function.
*
* @param updateFunction the update function
* @return the previous value
*/
@CanIgnoreReturnValue
public final double getAndUpdate(DoubleUnaryOperator updateFunction) {
while (true) {
long current = value;
double currentVal = longBitsToDouble(current);
double nextVal = currentVal + delta;
double nextVal = updateFunction.applyAsDouble(currentVal);
long next = doubleToRawLongBits(nextVal);
if (updater.compareAndSet(this, current, next)) {
return currentVal;
Expand All @@ -167,17 +221,17 @@ public final double getAndAdd(double delta) {
}

/**
* Atomically adds the given value to the current value.
* Atomically updates the current value with the results of applying the given function.
*
* @param delta the value to add
* @param updateFunction the update function
* @return the updated value
*/
@CanIgnoreReturnValue
public final double addAndGet(double delta) {
public final double updateAndGet(DoubleUnaryOperator updateFunction) {
while (true) {
long current = value;
double currentVal = longBitsToDouble(current);
double nextVal = currentVal + delta;
double nextVal = updateFunction.applyAsDouble(currentVal);
long next = doubleToRawLongBits(nextVal);
if (updater.compareAndSet(this, current, next)) {
return nextVal;
Expand Down
Loading

0 comments on commit 2d875d3

Please sign in to comment.