package com.googlecode.totallylazy;
import com.googlecode.totallylazy.functions.Function1;
import com.googlecode.totallylazy.predicates.Predicate;
import java.util.concurrent.Callable;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicReference;
import static com.googlecode.totallylazy.functions.Callables.returns;
import static com.googlecode.totallylazy.Callers.call;
import static com.googlecode.totallylazy.functions.Functions.function;
import static com.googlecode.totallylazy.predicates.Predicates.always;
public interface Atomic<T> extends Value<T> {
Atomic<T> modify(Function1<? super T, ? extends T> callable);
<R> R modifyReturn(Function1<? super T, ? extends Pair<? extends T, ? extends R>> callable);
class constructors {
public static <T> Atomic<T> atomic(final T t) {
return atomic(t, returns(always(Integer.class)));
}
public static <T> Atomic<T> atomic(final T t, final Callable<? extends Predicate<? super Integer>> retryPredicate) {
return new RetryingAtomic<T>(t, retryPredicate);
}
}
static class RetryingAtomic<T> implements Atomic<T> {
private final AtomicReference<T> reference;
private final Callable<? extends Predicate<? super Integer>> retryPredicate;
public RetryingAtomic(T t, Callable<? extends Predicate<? super Integer>> retryPredicate) {
this.retryPredicate = retryPredicate;
reference = new AtomicReference<T>(t);
}
@Override
public Atomic<T> modify(Function1<? super T, ? extends T> callable) {
return modifyReturn(function(callable).then(Pair.functions.<T, Atomic<T>>toPairWithSecond(this)));
}
@Override
public <R> R modifyReturn(Function1<? super T, ? extends Pair<? extends T, ? extends R>> callable) {
Predicate<? super Integer> retry = call(retryPredicate);
for (int i = 0; retry.matches(i); i++) {
T current = reference.get();
Pair<? extends T, ? extends R> modified = call(callable, current);
if (reference.compareAndSet(current, modified.first())) return modified.second() ;
}
throw new RejectedExecutionException(String.format("Atomic operation could not be applied due to %s", retry));
}
@Override
public T value() {
return reference.get();
}
}
}