net.uncontended.precipice.concurrent.Eventual Maven / Gradle / Ivy
/*
* Copyright 2014 Timothy Brooks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.uncontended.precipice.concurrent;
import net.uncontended.precipice.*;
import net.uncontended.precipice.ResultView;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
public class Eventual implements PrecipiceFuture, PrecipicePromise,
ExecutionContext {
private final long permitCount;
private final long startNanos;
private final Completable wrappedPromise;
private volatile V value;
private volatile Throwable throwable;
private volatile Cancellable cancellable;
private volatile boolean isCancelled = false;
private final CountDownLatch latch = new CountDownLatch(1);
private final AtomicReference result = new AtomicReference<>(null);
private final AtomicReference> successCallback = new AtomicReference<>();
private final AtomicReference> errorCallback = new AtomicReference<>();
private PrecipiceFunction internalCallback;
public Eventual() {
this(0L);
}
public Eventual(long permitCount) {
this(permitCount, System.nanoTime());
}
public Eventual(long permitCount, long startNanos) {
this(permitCount, startNanos, null);
}
public Eventual(Completable completable) {
this(0L, System.nanoTime(), completable);
}
public Eventual(long permitCount, long startNanos, Completable completable) {
this.permitCount = permitCount;
this.startNanos = startNanos;
wrappedPromise = completable;
}
@Override
public boolean complete(Result result, V value) {
if (this.result.get() == null) {
if (this.result.compareAndSet(null, result)) {
this.value = value;
executeInternalCallback(result);
latch.countDown();
PrecipiceFunction cb = successCallback.get();
if (cb != null && successCallback.compareAndSet(cb, null)) {
cb.apply(result, value);
}
if (wrappedPromise != null) {
wrappedPromise.complete(result, value);
}
return true;
}
}
return false;
}
@Override
public boolean completeExceptionally(Result result, Throwable exception) {
if (this.result.get() == null) {
if (this.result.compareAndSet(null, result)) {
throwable = exception;
executeInternalCallback(result);
latch.countDown();
PrecipiceFunction cb = errorCallback.get();
if (cb != null && errorCallback.compareAndSet(cb, null)) {
cb.apply(result, exception);
}
if (wrappedPromise != null) {
wrappedPromise.completeExceptionally(result, exception);
}
return true;
}
}
return false;
}
@Override
public ResultView resultView() {
return this;
}
@Override
public PrecipiceFuture future() {
return this;
}
@Override
public V get() throws InterruptedException, ExecutionException {
latch.await();
if (value != null) {
return value;
} else if (isCancelled()) {
throw new CancellationException();
} else {
throw new ExecutionException(throwable);
}
}
@Override
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
if (latch.await(timeout, unit)) {
if (value != null) {
return value;
} else if (isCancelled()) {
throw new CancellationException();
} else {
throw new ExecutionException(throwable);
}
} else {
throw new TimeoutException();
}
}
@Override
public boolean isDone() {
return result.get() != null;
}
@Override
public boolean isCancelled() {
return isCancelled;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
if (mayInterruptIfRunning && cancellable != null && !isDone()) {
isCancelled = true;
cancellable.cancel();
return true;
} else {
return false;
}
}
@Override
public void await() throws InterruptedException {
latch.await();
}
@Override
public void await(long duration, TimeUnit unit) throws InterruptedException {
latch.await(duration, unit);
}
@Override
public V getValue() {
return value;
}
@Override
public Throwable getError() {
return throwable;
}
@Override
public void onSuccess(PrecipiceFunction fn) {
// TODO: Decide whether it is okay to execute multiple callbacks.
Result localResult = result.get();
if (localResult != null && !localResult.isFailure()) {
fn.apply(localResult, value);
} else {
if (successCallback.compareAndSet(null, fn)) {
Result localResult2 = result.get();
if (localResult2 != null && !localResult2.isFailure() && successCallback.compareAndSet(fn, null)) {
fn.apply(localResult2, value);
}
}
}
}
@Override
public void onError(PrecipiceFunction fn) {
Result localResult = result.get();
if (localResult != null && localResult.isFailure()) {
fn.apply(localResult, throwable);
} else {
if (errorCallback.compareAndSet(null, fn)) {
Result localResult2 = result.get();
if (localResult2 != null && localResult2.isFailure() && errorCallback.compareAndSet(fn, null)) {
fn.apply(localResult2, throwable);
}
}
}
}
@Override
public Result getResult() {
return result.get();
}
@Override
public long startNanos() {
return startNanos;
}
@Override
public long permitCount() {
return permitCount;
}
public void setCancellable(Cancellable cancellable) {
this.cancellable = cancellable;
}
public void internalOnComplete(PrecipiceFunction fn) {
internalCallback = fn;
}
private void executeInternalCallback(Result result) {
if (internalCallback != null) {
internalCallback.apply(result, this);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy