DATAREDIS-874 - Implement accumulate/update methods on RedisAtomic[*] classes.

This commit adds the following public methods to RedisAtomicInteger,
RedisAtomicLong, and RedisAtomicDouble:

T accumulateAndGet(T updateValue, BinaryOperator<T> accumulatorFunction);
T getAndAccumulate(T updateValue, BinaryOperator<T> accumulatorFunction);
T updateAndGet(UnaryOperator<T> updateFunction);
T getAndUpdate(UnaryOperator<T> updateFunction);

These methods are primarily useful for doing CAS operations with the
help of Binary/UnaryOperators to generate the new value. They mirror
their counterparts on the AtomicInteger and AtomicLong classes
available in the java.util.concurrent.atomic package.

Original pull request: #362.
This commit is contained in:
Graham MacMaster
2018-09-21 23:01:14 -04:00
committed by Mark Paluch
parent 7ab8ade8d3
commit f2107ef81b
6 changed files with 524 additions and 0 deletions

View File

@@ -20,6 +20,9 @@ import static org.assertj.core.api.Assertions.*;
import java.util.Collection;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.DoubleUnaryOperator;
import java.util.function.DoubleBinaryOperator;
import org.assertj.core.data.Offset;
import org.junit.After;
@@ -43,6 +46,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
* @author Graham MacMaster
*/
@RunWith(Parameterized.class)
public class RedisAtomicDoubleTests extends AbstractRedisAtomicsTests {
@@ -225,4 +229,96 @@ public class RedisAtomicDoubleTests extends AbstractRedisAtomicsTests {
assertThat(test.getAndSet(2)).isZero();
}
@Test // DATAREDIS-874
public void updateAndGetAppliesGivenUpdateFunctionAndReturnsUpdatedValue() {
// Arrange
AtomicBoolean operatorHasBeenApplied = new AtomicBoolean(false);
double initialValue = 5.3;
double expectedNewValue = 10.6;
doubleCounter.set(initialValue);
DoubleUnaryOperator updateFunction = input -> {
operatorHasBeenApplied.set(true);
return expectedNewValue;
};
// Act
double result = doubleCounter.updateAndGet(updateFunction);
// Assert
assertThat(result).isEqualTo(expectedNewValue);
assertThat(doubleCounter.get()).isEqualTo(expectedNewValue);
assertThat(operatorHasBeenApplied.get()).isTrue();
}
@Test // DATAREDIS-874
public void getAndUpdateAppliesGivenUpdateFunctionAndReturnsOriginalValue() {
// Arrange
AtomicBoolean operatorHasBeenApplied = new AtomicBoolean(false);
double initialValue = 5.3;
double expectedNewValue = 10.6;
doubleCounter.set(initialValue);
DoubleUnaryOperator updateFunction = input -> {
operatorHasBeenApplied.set(true);
return expectedNewValue;
};
// Act
double result = doubleCounter.getAndUpdate(updateFunction);
// Assert
assertThat(result).isEqualTo(initialValue);
assertThat(doubleCounter.get()).isEqualTo(expectedNewValue);
assertThat(operatorHasBeenApplied.get()).isTrue();
}
@Test // DATAREDIS-874
public void accumulateAndGetAppliesGivenAccumulatorFunctionAndReturnsUpdatedValue() {
// Arrange
AtomicBoolean operatorHasBeenApplied = new AtomicBoolean(false);
double initialValue = 5.3;
double expectedNewValue = 10.6;
doubleCounter.set(initialValue);
DoubleBinaryOperator accumulatorFunction = (x, y) -> {
operatorHasBeenApplied.set(true);
return expectedNewValue;
};
// Act
double result = doubleCounter.accumulateAndGet(15.9, accumulatorFunction);
// Assert
assertThat(result).isEqualTo(expectedNewValue);
assertThat(doubleCounter.get()).isEqualTo(expectedNewValue);
assertThat(operatorHasBeenApplied.get()).isTrue();
}
@Test // DATAREDIS-874
public void getAndAccumulateAppliesGivenAccumulatorFunctionAndReturnsOriginalValue() {
// Arrange
AtomicBoolean operatorHasBeenApplied = new AtomicBoolean(false);
double initialValue = 5.3;
double expectedNewValue = 10.6;
doubleCounter.set(initialValue);
DoubleBinaryOperator accumulatorFunction = (x, y) -> {
operatorHasBeenApplied.set(true);
return expectedNewValue;
};
// Act
double result = doubleCounter.getAndAccumulate(15.9, accumulatorFunction);
// Assert
assertThat(result).isEqualTo(initialValue);
assertThat(doubleCounter.get()).isEqualTo(expectedNewValue);
assertThat(operatorHasBeenApplied.get()).isTrue();
}
}

View File

@@ -20,6 +20,8 @@ import static org.assertj.core.api.Assertions.*;
import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;
import org.junit.After;
import org.junit.AfterClass;
@@ -43,6 +45,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
* @author Graham MacMaster
*/
@RunWith(Parameterized.class)
public class RedisAtomicIntegerTests extends AbstractRedisAtomicsTests {
@@ -233,4 +236,96 @@ public class RedisAtomicIntegerTests extends AbstractRedisAtomicsTests {
assertThat(test.getAndSet(2)).isZero();
}
@Test // DATAREDIS-874
public void updateAndGetAppliesGivenUpdateFunctionAndReturnsUpdatedValue() {
// Arrange
AtomicBoolean operatorHasBeenApplied = new AtomicBoolean(false);
int initialValue = 5;
int expectedNewValue = 10;
intCounter.set(initialValue);
IntUnaryOperator updateFunction = input -> {
operatorHasBeenApplied.set(true);
return expectedNewValue;
};
// Act
int result = intCounter.updateAndGet(updateFunction);
// Assert
assertThat(result).isEqualTo(expectedNewValue);
assertThat(intCounter.get()).isEqualTo(expectedNewValue);
assertThat(operatorHasBeenApplied.get()).isTrue();
}
@Test // DATAREDIS-874
public void getAndUpdateAppliesGivenUpdateFunctionAndReturnsOriginalValue() {
// Arrange
AtomicBoolean operatorHasBeenApplied = new AtomicBoolean(false);
int initialValue = 5;
int expectedNewValue = 10;
intCounter.set(initialValue);
IntUnaryOperator updateFunction = input -> {
operatorHasBeenApplied.set(true);
return expectedNewValue;
};
// Act
int result = intCounter.getAndUpdate(updateFunction);
// Assert
assertThat(result).isEqualTo(initialValue);
assertThat(intCounter.get()).isEqualTo(expectedNewValue);
assertThat(operatorHasBeenApplied.get()).isTrue();
}
@Test // DATAREDIS-874
public void accumulateAndGetAppliesGivenAccumulatorFunctionAndReturnsUpdatedValue() {
// Arrange
AtomicBoolean operatorHasBeenApplied = new AtomicBoolean(false);
int initialValue = 5;
int expectedNewValue = 10;
intCounter.set(initialValue);
IntBinaryOperator accumulatorFunction = (x, y) -> {
operatorHasBeenApplied.set(true);
return expectedNewValue;
};
// Act
int result = intCounter.accumulateAndGet(15, accumulatorFunction);
// Assert
assertThat(result).isEqualTo(expectedNewValue);
assertThat(intCounter.get()).isEqualTo(expectedNewValue);
assertThat(operatorHasBeenApplied.get()).isTrue();
}
@Test // DATAREDIS-874
public void getAndAccumulateAppliesGivenAccumulatorFunctionAndReturnsOriginalValue() {
// Arrange
AtomicBoolean operatorHasBeenApplied = new AtomicBoolean(false);
int initialValue = 5;
int expectedNewValue = 10;
intCounter.set(initialValue);
IntBinaryOperator accumulatorFunction = (x, y) -> {
operatorHasBeenApplied.set(true);
return expectedNewValue;
};
// Act
int result = intCounter.getAndAccumulate(15, accumulatorFunction);
// Assert
assertThat(result).isEqualTo(initialValue);
assertThat(intCounter.get()).isEqualTo(expectedNewValue);
assertThat(operatorHasBeenApplied.get()).isTrue();
}
}

View File

@@ -18,6 +18,9 @@ package org.springframework.data.redis.support.atomic;
import static org.assertj.core.api.Assertions.*;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.LongBinaryOperator;
import java.util.function.LongUnaryOperator;
import org.junit.After;
import org.junit.AfterClass;
@@ -41,6 +44,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
* @author Graham MacMaster
*/
@RunWith(Parameterized.class)
public class RedisAtomicLongTests extends AbstractRedisAtomicsTests {
@@ -214,4 +218,96 @@ public class RedisAtomicLongTests extends AbstractRedisAtomicsTests {
assertThat(test.getAndSet(2)).isZero();
}
@Test // DATAREDIS-874
public void updateAndGetAppliesGivenUpdateFunctionAndReturnsUpdatedValue() {
// Arrange
AtomicBoolean operatorHasBeenApplied = new AtomicBoolean(false);
long initialValue = 5;
long expectedNewValue = 10;
longCounter.set(initialValue);
LongUnaryOperator updateFunction = input -> {
operatorHasBeenApplied.set(true);
return expectedNewValue;
};
// Act
long result = longCounter.updateAndGet(updateFunction);
// Assert
assertThat(result).isEqualTo(expectedNewValue);
assertThat(longCounter.get()).isEqualTo(expectedNewValue);
assertThat(operatorHasBeenApplied.get()).isTrue();
}
@Test // DATAREDIS-874
public void getAndUpdateAppliesGivenUpdateFunctionAndReturnsOriginalValue() {
// Arrange
AtomicBoolean operatorHasBeenApplied = new AtomicBoolean(false);
long initialValue = 5;
long expectedNewValue = 10;
longCounter.set(initialValue);
LongUnaryOperator updateFunction = input -> {
operatorHasBeenApplied.set(true);
return expectedNewValue;
};
// Act
long result = longCounter.getAndUpdate(updateFunction);
// Assert
assertThat(result).isEqualTo(initialValue);
assertThat(longCounter.get()).isEqualTo(expectedNewValue);
assertThat(operatorHasBeenApplied.get()).isTrue();
}
@Test // DATAREDIS-874
public void accumulateAndGetAppliesGivenAccumulatorFunctionAndReturnsUpdatedValue() {
// Arrange
AtomicBoolean operatorHasBeenApplied = new AtomicBoolean(false);
long initialValue = 5;
long expectedNewValue = 10;
longCounter.set(initialValue);
LongBinaryOperator accumulatorFunction = (x, y) -> {
operatorHasBeenApplied.set(true);
return expectedNewValue;
};
// Act
long result = longCounter.accumulateAndGet(15L, accumulatorFunction);
// Assert
assertThat(result).isEqualTo(expectedNewValue);
assertThat(longCounter.get()).isEqualTo(expectedNewValue);
assertThat(operatorHasBeenApplied.get()).isTrue();
}
@Test // DATAREDIS-874
public void getAndAccumulateAppliesGivenAccumulatorFunctionAndReturnsOriginalValue() {
// Arrange
AtomicBoolean operatorHasBeenApplied = new AtomicBoolean(false);
long initialValue = 5;
long expectedNewValue = 10;
longCounter.set(initialValue);
LongBinaryOperator accumulatorFunction = (x, y) -> {
operatorHasBeenApplied.set(true);
return expectedNewValue;
};
// Act
long result = longCounter.getAndAccumulate(15L, accumulatorFunction);
// Assert
assertThat(result).isEqualTo(initialValue);
assertThat(longCounter.get()).isEqualTo(expectedNewValue);
assertThat(operatorHasBeenApplied.get()).isTrue();
}
}