Skip to content

Commit 2d875d3

Browse files
allanhaywoodGoogle Java Core Libraries
authored andcommitted
Implemented the following methods for
`AtomicDouble` and `AtomicDoubleArray`: * `accumulateAndGet` * `getAndAccumulate` * `updateAndGet` * `getAndUpdate` Closes #5784. Fixes #5742. RELNOTES=Implement accumulate/update methods for `AtomicDouble` and `AtomicDoubleArray`. PiperOrigin-RevId: 412110543
1 parent bdb43e6 commit 2d875d3

File tree

4 files changed

+365
-13
lines changed

4 files changed

+365
-13
lines changed

guava-tests/test/com/google/common/util/concurrent/AtomicDoubleArrayTest.java

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313

1414
package com.google.common.util.concurrent;
1515

16+
import static java.lang.Math.max;
17+
18+
import com.google.common.annotations.GwtIncompatible;
19+
import com.google.common.testing.NullPointerTester;
1620
import java.util.Arrays;
1721

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

55+
@GwtIncompatible // NullPointerTester
56+
public void testNulls() {
57+
new NullPointerTester().testAllPublicStaticMethods(AtomicDoubleArray.class);
58+
new NullPointerTester().testAllPublicConstructors(AtomicDoubleArray.class);
59+
new NullPointerTester().testAllPublicInstanceMethods(new AtomicDoubleArray(1));
60+
}
61+
5162
/** constructor creates array of given size with all elements zero */
5263
public void testConstructor() {
5364
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
@@ -211,7 +222,8 @@ public void testWeakCompareAndSet() {
211222
assertBitEquals(prev, aa.get(i));
212223
assertFalse(aa.weakCompareAndSet(i, unused, x));
213224
assertBitEquals(prev, aa.get(i));
214-
while (!aa.weakCompareAndSet(i, prev, x)) {;
225+
while (!aa.weakCompareAndSet(i, prev, x)) {
226+
;
215227
}
216228
assertBitEquals(x, aa.get(i));
217229
prev = x;
@@ -261,6 +273,128 @@ public void testAddAndGet() {
261273
}
262274
}
263275

276+
/** getAndAccumulate with sum adds given value to current, and returns previous value */
277+
public void testGetAndAccumulateWithSum() {
278+
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
279+
for (int i : new int[] {0, SIZE - 1}) {
280+
for (double x : VALUES) {
281+
for (double y : VALUES) {
282+
aa.set(i, x);
283+
double z = aa.getAndAccumulate(i, y, Double::sum);
284+
assertBitEquals(x, z);
285+
assertBitEquals(x + y, aa.get(i));
286+
}
287+
}
288+
}
289+
}
290+
291+
/** getAndAccumulate with max stores max of given value to current, and returns previous value */
292+
public void testGetAndAccumulateWithMax() {
293+
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
294+
for (int i : new int[] {0, SIZE - 1}) {
295+
for (double x : VALUES) {
296+
for (double y : VALUES) {
297+
aa.set(i, x);
298+
double z = aa.getAndAccumulate(i, y, Double::max);
299+
double expectedMax = max(x, y);
300+
assertBitEquals(x, z);
301+
assertBitEquals(expectedMax, aa.get(i));
302+
}
303+
}
304+
}
305+
}
306+
307+
/** accumulateAndGet with sum adds given value to current, and returns current value */
308+
public void testAccumulateAndGetWithSum() {
309+
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
310+
for (int i : new int[] {0, SIZE - 1}) {
311+
for (double x : VALUES) {
312+
for (double y : VALUES) {
313+
aa.set(i, x);
314+
double z = aa.accumulateAndGet(i, y, Double::sum);
315+
assertBitEquals(x + y, z);
316+
assertBitEquals(x + y, aa.get(i));
317+
}
318+
}
319+
}
320+
}
321+
322+
/** accumulateAndGet with max stores max of given value to current, and returns current value */
323+
public void testAccumulateAndGetWithMax() {
324+
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
325+
for (int i : new int[] {0, SIZE - 1}) {
326+
for (double x : VALUES) {
327+
for (double y : VALUES) {
328+
aa.set(i, x);
329+
double z = aa.accumulateAndGet(i, y, Double::max);
330+
double expectedMax = max(x, y);
331+
assertBitEquals(expectedMax, z);
332+
assertBitEquals(expectedMax, aa.get(i));
333+
}
334+
}
335+
}
336+
}
337+
338+
/** getAndUpdate adds given value to current, and returns previous value */
339+
public void testGetAndUpdateWithSum() {
340+
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
341+
for (int i : new int[] {0, SIZE - 1}) {
342+
for (double x : VALUES) {
343+
for (double y : VALUES) {
344+
aa.set(i, x);
345+
double z = aa.getAndUpdate(i, value -> value + y);
346+
assertBitEquals(x, z);
347+
assertBitEquals(x + y, aa.get(i));
348+
}
349+
}
350+
}
351+
}
352+
353+
/** getAndUpdate subtracts given value to current, and returns previous value */
354+
public void testGetAndUpdateWithSubtract() {
355+
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
356+
for (int i : new int[] {0, SIZE - 1}) {
357+
for (double x : VALUES) {
358+
for (double y : VALUES) {
359+
aa.set(i, x);
360+
double z = aa.getAndUpdate(i, value -> value - y);
361+
assertBitEquals(x, z);
362+
assertBitEquals(x - y, aa.get(i));
363+
}
364+
}
365+
}
366+
}
367+
368+
/** updateAndGet adds given value to current, and returns current value */
369+
public void testUpdateAndGetWithSum() {
370+
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
371+
for (int i : new int[] {0, SIZE - 1}) {
372+
for (double x : VALUES) {
373+
for (double y : VALUES) {
374+
aa.set(i, x);
375+
double z = aa.updateAndGet(i, value -> value + y);
376+
assertBitEquals(x + y, z);
377+
assertBitEquals(x + y, aa.get(i));
378+
}
379+
}
380+
}
381+
}
382+
383+
/** updateAndGet subtracts given value to current, and returns current value */
384+
public void testUpdateAndGetWithSubtract() {
385+
AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
386+
for (int i : new int[] {0, SIZE - 1}) {
387+
for (double x : VALUES) {
388+
for (double y : VALUES) {
389+
aa.set(i, x);
390+
double z = aa.updateAndGet(i, value -> value - y);
391+
assertBitEquals(x - y, z);
392+
assertBitEquals(x - y, aa.get(i));
393+
}
394+
}
395+
}
396+
}
397+
264398
static final long COUNTDOWN = 100000;
265399

266400
class Counter extends CheckedRunnable {

guava-tests/test/com/google/common/util/concurrent/AtomicDoubleTest.java

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
package com.google.common.util.concurrent;
1515

16+
import static java.lang.Math.max;
17+
1618

1719
/** Unit test for {@link AtomicDouble}. */
1820
public class AtomicDoubleTest extends JSR166TestCase {
@@ -125,7 +127,8 @@ public void testWeakCompareAndSet() {
125127
assertBitEquals(prev, at.get());
126128
assertFalse(at.weakCompareAndSet(unused, x));
127129
assertBitEquals(prev, at.get());
128-
while (!at.weakCompareAndSet(prev, x)) {;
130+
while (!at.weakCompareAndSet(prev, x)) {
131+
;
129132
}
130133
assertBitEquals(x, at.get());
131134
prev = x;
@@ -166,6 +169,108 @@ public void testAddAndGet() {
166169
}
167170
}
168171

172+
/** getAndAccumulate with sum adds given value to current, and returns previous value */
173+
public void testGetAndAccumulateWithSum() {
174+
for (double x : VALUES) {
175+
for (double y : VALUES) {
176+
AtomicDouble a = new AtomicDouble(x);
177+
double z = a.getAndAccumulate(y, Double::sum);
178+
assertBitEquals(x, z);
179+
assertBitEquals(x + y, a.get());
180+
}
181+
}
182+
}
183+
184+
/** getAndAccumulate with max stores max of given value to current, and returns previous value */
185+
public void testGetAndAccumulateWithMax() {
186+
for (double x : VALUES) {
187+
for (double y : VALUES) {
188+
AtomicDouble a = new AtomicDouble(x);
189+
double z = a.getAndAccumulate(y, Double::max);
190+
double expectedMax = max(x, y);
191+
assertBitEquals(x, z);
192+
assertBitEquals(expectedMax, a.get());
193+
}
194+
}
195+
}
196+
197+
/** accumulateAndGet with sum adds given value to current, and returns current value */
198+
public void testAccumulateAndGetWithSum() {
199+
for (double x : VALUES) {
200+
for (double y : VALUES) {
201+
AtomicDouble a = new AtomicDouble(x);
202+
double z = a.accumulateAndGet(y, Double::sum);
203+
assertBitEquals(x + y, z);
204+
assertBitEquals(x + y, a.get());
205+
}
206+
}
207+
}
208+
209+
/** accumulateAndGet with max stores max of given value to current, and returns current value */
210+
public void testAccumulateAndGetWithMax() {
211+
for (double x : VALUES) {
212+
for (double y : VALUES) {
213+
AtomicDouble a = new AtomicDouble(x);
214+
double z = a.accumulateAndGet(y, Double::max);
215+
double expectedMax = max(x, y);
216+
assertBitEquals(expectedMax, z);
217+
assertBitEquals(expectedMax, a.get());
218+
}
219+
}
220+
}
221+
222+
/** getAndUpdate with sum stores sum of given value to current, and returns previous value */
223+
public void testGetAndUpdateWithSum() {
224+
for (double x : VALUES) {
225+
for (double y : VALUES) {
226+
AtomicDouble a = new AtomicDouble(x);
227+
double z = a.getAndUpdate(value -> value + y);
228+
assertBitEquals(x, z);
229+
assertBitEquals(x + y, a.get());
230+
}
231+
}
232+
}
233+
234+
/**
235+
* getAndUpdate with subtract stores subtraction of value from current, and returns previous value
236+
*/
237+
public void testGetAndUpdateWithSubtract() {
238+
for (double x : VALUES) {
239+
for (double y : VALUES) {
240+
AtomicDouble a = new AtomicDouble(x);
241+
double z = a.getAndUpdate(value -> value - y);
242+
assertBitEquals(x, z);
243+
assertBitEquals(x - y, a.get());
244+
}
245+
}
246+
}
247+
248+
/** updateAndGet with sum stores sum of given value to current, and returns current value */
249+
public void testUpdateAndGetWithSum() {
250+
for (double x : VALUES) {
251+
for (double y : VALUES) {
252+
AtomicDouble a = new AtomicDouble(x);
253+
double z = a.updateAndGet(value -> value + y);
254+
assertBitEquals(x + y, z);
255+
assertBitEquals(x + y, a.get());
256+
}
257+
}
258+
}
259+
260+
/**
261+
* updateAndGet with subtract stores subtraction of value from current, and returns current value
262+
*/
263+
public void testUpdateAndGetWithSubtract() {
264+
for (double x : VALUES) {
265+
for (double y : VALUES) {
266+
AtomicDouble a = new AtomicDouble(x);
267+
double z = a.updateAndGet(value -> value - y);
268+
assertBitEquals(x - y, z);
269+
assertBitEquals(x - y, a.get());
270+
}
271+
}
272+
}
273+
169274
/** a deserialized serialized atomic holds same value */
170275
public void testSerialization() throws Exception {
171276
AtomicDouble a = new AtomicDouble();

guava/src/com/google/common/util/concurrent/AtomicDouble.java

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,16 @@
1414

1515
package com.google.common.util.concurrent;
1616

17+
import static com.google.common.base.Preconditions.checkNotNull;
1718
import static java.lang.Double.doubleToRawLongBits;
1819
import static java.lang.Double.longBitsToDouble;
1920

2021
import com.google.common.annotations.GwtIncompatible;
2122
import com.google.errorprone.annotations.CanIgnoreReturnValue;
2223
import com.google.j2objc.annotations.ReflectionSupport;
2324
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
25+
import java.util.function.DoubleBinaryOperator;
26+
import java.util.function.DoubleUnaryOperator;
2427

2528
/**
2629
* A {@code double} value that may be updated atomically. See the {@link
@@ -155,10 +158,61 @@ public final boolean weakCompareAndSet(double expect, double update) {
155158
*/
156159
@CanIgnoreReturnValue
157160
public final double getAndAdd(double delta) {
161+
return getAndAccumulate(delta, Double::sum);
162+
}
163+
164+
/**
165+
* Atomically adds the given value to the current value.
166+
*
167+
* @param delta the value to add
168+
* @return the updated value
169+
*/
170+
@CanIgnoreReturnValue
171+
public final double addAndGet(double delta) {
172+
return accumulateAndGet(delta, Double::sum);
173+
}
174+
175+
/**
176+
* Atomically updates the current value with the results of applying the given function to the
177+
* current and given values.
178+
*
179+
* @param x the update value
180+
* @param accumulatorFunction the accumulator function
181+
* @return the previous value
182+
*/
183+
@CanIgnoreReturnValue
184+
public final double getAndAccumulate(double x, DoubleBinaryOperator accumulatorFunction) {
185+
checkNotNull(accumulatorFunction);
186+
return getAndUpdate(oldValue -> accumulatorFunction.applyAsDouble(oldValue, x));
187+
}
188+
189+
/**
190+
* Atomically updates the current value with the results of applying the given function to the
191+
* current and given values.
192+
*
193+
* @param x the update value
194+
* @param accumulatorFunction the accumulator function
195+
* @return the updated value
196+
* @since NEXT
197+
*/
198+
@CanIgnoreReturnValue
199+
public final double accumulateAndGet(double x, DoubleBinaryOperator accumulatorFunction) {
200+
checkNotNull(accumulatorFunction);
201+
return updateAndGet(oldValue -> accumulatorFunction.applyAsDouble(oldValue, x));
202+
}
203+
204+
/**
205+
* Atomically updates the current value with the results of applying the given function.
206+
*
207+
* @param updateFunction the update function
208+
* @return the previous value
209+
*/
210+
@CanIgnoreReturnValue
211+
public final double getAndUpdate(DoubleUnaryOperator updateFunction) {
158212
while (true) {
159213
long current = value;
160214
double currentVal = longBitsToDouble(current);
161-
double nextVal = currentVal + delta;
215+
double nextVal = updateFunction.applyAsDouble(currentVal);
162216
long next = doubleToRawLongBits(nextVal);
163217
if (updater.compareAndSet(this, current, next)) {
164218
return currentVal;
@@ -167,17 +221,17 @@ public final double getAndAdd(double delta) {
167221
}
168222

169223
/**
170-
* Atomically adds the given value to the current value.
224+
* Atomically updates the current value with the results of applying the given function.
171225
*
172-
* @param delta the value to add
226+
* @param updateFunction the update function
173227
* @return the updated value
174228
*/
175229
@CanIgnoreReturnValue
176-
public final double addAndGet(double delta) {
230+
public final double updateAndGet(DoubleUnaryOperator updateFunction) {
177231
while (true) {
178232
long current = value;
179233
double currentVal = longBitsToDouble(current);
180-
double nextVal = currentVal + delta;
234+
double nextVal = updateFunction.applyAsDouble(currentVal);
181235
long next = doubleToRawLongBits(nextVal);
182236
if (updater.compareAndSet(this, current, next)) {
183237
return nextVal;

0 commit comments

Comments
 (0)