io.atlassian.util.concurrent.Expiring Maven / Gradle / Ivy
Show all versions of atlassian-util-concurrent Show documentation
package io.atlassian.util.concurrent;
import io.atlassian.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import java.util.function.Supplier;
import static java.util.Objects.requireNonNull;
/**
* A Reference that can expire based on some strategy.
*
* The strategy is a continuous supply of Predicates that will remain true until
* the reference has expired, from which point they should always fail. A new
* reference will then be
*
* @param the type of reference held.
*
* @since 2.1
*/
final class Expiring implements Supplier {
private final AtomicReference> r = new AtomicReference>(Dead. instance());
private final Supplier factory;
private final Supplier> strategy;
Expiring(final Supplier factory, final Supplier> strategy) {
this.factory = requireNonNull(factory);
this.strategy = requireNonNull(strategy);
}
/** {@inheritDoc} */
@Override public T get() {
int i = 0;
while (true) {
final Alive e = r.get();
if (e.alive()) {
return e.get();
}
if (i++ > 100) {
// infinite loop detection, must halt
throw new AssertionError(100 + " attempts to CAS update the next value, aborting!");
}
r.compareAndSet(e, new Value());
}
}
//
// inner classes
//
/**
* Get a value and let us know whether it should still be current/alive.
*/
interface Alive extends Supplier {
boolean alive();
}
/**
* Holds a value and the liveness predicate.
*
* Lazily computes the value so the construction is cheap and fast.
*/
final class Value extends LazyReference implements Alive {
final Predicate alive = requireNonNull(strategy.get());
@Override public boolean alive() {
return alive.test(null);
}
@Override public T create() {
return factory.get();
}
}
/**
* Initial state is dead.
*/
enum Dead implements Alive