Commit bba0e55a authored by Rajiv Prabhakar's avatar Rajiv Prabhakar
Browse files

Tmp

parent 7934fdcc
Pipeline #64233327 failed with stage
in 1 minute and 43 seconds
package org.rajivprab.cava;
import org.apache.commons.lang3.Validate;
import com.google.common.base.Suppliers;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicReference;
......@@ -13,8 +13,6 @@ import java.util.function.Supplier;
* and only if no other value was previously set.
*
* Before the first get() call, the data can be set() to any arbitrary value.
* It is the caller's responsibility to ensure that any calls to set() match any previous set() or get() calls made.
* Else, an exception will be thrown, and the newly set value will be ignored.
*
* This class does not support setting/getting null values.
* It's ok for no default to be given, or for the default-supplier to return a null,
......@@ -30,9 +28,9 @@ public class DynamicConstant<T> {
// https://stackoverflow.com/questions/14676997/java-memory-visibility-and-atomicreferences
// https://stackoverflow.com/questions/3964211/when-to-use-atomicreference-in-java
private final AtomicReference<T> value = new AtomicReference<>();
private final Supplier<T> defaultSupplier;
private T defaultSupplied;
// Suppliers.memoize is thread-safe and guarantees that supplier will be successfully invoked only once
private final Supplier<T> defaultSupplier;
// ------------- Constructors -----------
......@@ -49,40 +47,52 @@ public class DynamicConstant<T> {
}
private DynamicConstant(Supplier<T> defaultSupplier) {
this.defaultSupplier = Validatec.notNull(defaultSupplier, "Cannot have a null supplier");
Validatec.notNull(defaultSupplier, "Cannot have a null supplier");
this.defaultSupplier = Suppliers.memoize(defaultSupplier::get);
}
// --------------------------------------
// Returns the most recently set value, or if never set, the default.
// Throws NoSuchElementException if default was not provided, and set was never called.
/**
* @return the most recently set value, or if never set, the default.
* @throws NoSuchElementException if default was not provided, and set was never called.
* @throws IllegalArgumentException if the default is a null
*
* Note: it's possible for the default to be constructed, but remain unused,
* under certain race conditions with other set() calls.
*/
public T get() {
T value = this.value.get();
return value == null ? setDefault() : value;
if (value != null) {
return value;
}
T def = Validatec.notNull(defaultSupplier.get(), "Default value should not be null");
return attemptSet(def);
}
// Returns true if the value has been permanently set to any user-supplied value, or the default
// If value has never been set, the default is not invoked
// Returns true if the value has been permanently set to any user-supplied value, or the default.
// If value has never been set, the default is not invoked.
public boolean isSet() {
return value.get() != null;
}
// If set is called with data that is incompatible with the previous get/set calls,
// An exception will be thrown, and no data will be changed.
public T set(T newVal) {
Validatec.notNull(newVal, "Cannot set value to null");
boolean updated = value.compareAndSet(null, newVal);
Validate.isTrue(updated || value.get().equals(newVal),
"New value: %s, does not match existing value: %s", newVal, value.get());
return newVal;
/**
* If called with value that is not equal to previously set value,
* an exception will be thrown, and no data will be changed.
*
* If called with an instance that is different from an existing instance,
* but equivalent to it, no exception will be thrown, and no data will be changed.
* Ie, "abc" != "abc" but "abc".equals("abc")
*/
public void set(T newVal) {
T set = attemptSet(newVal);
Validatec.equals(set, newVal, String.format("New value: %s, does not match existing value: %s", newVal, set));
}
private synchronized T setDefault() {
if (defaultSupplied == null) {
defaultSupplied = Validatec.notNull(defaultSupplier.get(), "Default value should not be null");
return set(defaultSupplied);
} else {
return defaultSupplied;
}
// Attempts to set the value, and regardless of success, will return the value that is finally stored
public T attemptSet(T newVal) {
Validatec.notNull(newVal, "Cannot set value to null");
boolean updated = value.compareAndSet(null, newVal);
return updated ? newVal : value.get();
}
}
\ No newline at end of file
package org.rajivprab.cava;
import com.google.common.collect.ImmutableList;
import org.junit.Assert;
import org.junit.Test;
import org.rajivprab.cava.exception.InterruptedExceptionc;
......@@ -147,13 +146,15 @@ public class DynamicConstantTest extends TestBase {
CountDownLatch latch = new CountDownLatch(numThreads);
Set<Integer> values = IntStream.range(0, numThreads)
.mapToObj(i -> ThreadUtilc.fork(() -> getAfterLatchCountdown(constant, latch)))
.collect(ImmutableList.toImmutableList()).stream()
.collect(Collectors.toList()).stream()
.map(ThreadUtilc::get)
.collect(Collectors.toSet());
assertThat(values).hasSize(1);
assertThat(RandomInt.numCalls.get()).isEqualTo(1);
}
// ------------------------
private static <T> T getAfterLatchCountdown(DynamicConstant<T> constant, CountDownLatch latch) {
try {
latch.countDown();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment