WaarpFuture.java
/*
* This file is part of Waarp Project (named also Waarp or GG).
*
* Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
* tags. See the COPYRIGHT.txt in the distribution for a full listing of
* individual contributors.
*
* All Waarp Project is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Waarp . If not, see <http://www.gnu.org/licenses/>.
*/
package org.waarp.common.future;
import org.waarp.common.logging.SysErrLogger;
import java.util.concurrent.TimeUnit;
import static java.util.concurrent.TimeUnit.*;
/**
* Ftp Future operation<br>
* Completely inspired from the excellent ChannelFuture of Netty, but without
* any channel inside.
*/
public class WaarpFuture implements WaarpFutureInterface {
private static final Throwable CANCELLED = new Throwable();
private final boolean cancellable;
private volatile boolean done;
private Throwable cause;
private int waiters;
/**
* Creates a new instance.
*/
public WaarpFuture() {
cancellable = false;
}
/**
* Creates a new instance.
*
* @param cancellable {@code true} if and only if this future can be
* canceled
*/
public WaarpFuture(final boolean cancellable) {
this.cancellable = cancellable;
}
/**
* Returns {@code true} if and only if this future is complete, regardless
* of
* whether the operation was
* successful, failed, or canceled.
*
* @return True if the future is complete
*/
@Override
public final boolean isDone() {
return done;
}
/**
* Returns {@code true} if and only if the operation was completed
* successfully.
*
* @return True if the future is successful
*/
@Override
public final boolean isSuccess() {
return done && cause == null;
}
/**
* Returns {@code true} if and only if the operation was completed but
* unsuccessfully.
*
* @return True if the future is done but unsuccessful
*/
@Override
public final boolean isFailed() {
return done && cause != null;
}
/**
* Returns the cause of the failed operation if the operation has failed.
*
* @return the cause of the failure. {@code null} if succeeded or this
* future
* is not completed yet.
*/
@Override
public final Throwable getCause() {
if (cause != CANCELLED) {
return cause;
}
return null;
}
/**
* Returns {@code true} if and only if this future was canceled by a {@link
* #cancel()} method.
*
* @return True if the future was canceled
*/
@Override
public final boolean isCancelled() {
return done && cause == CANCELLED;
}
/**
* Rethrows the exception that caused this future fail if this future is
* complete and failed.
*/
@Override
public final WaarpFuture rethrowIfFailed() throws Exception {
if (!isDone()) {
return this;
}
final Throwable causeNew = getCause();
if (causeNew == null) {
return this;
}
if (causeNew instanceof Exception) {
throw (Exception) causeNew;
}
if (causeNew instanceof Error) {
throw (Error) causeNew;
}
throw new RuntimeException(causeNew);
}
/**
* @return True if the Future is done or False if interrupted
*/
@Override
public final boolean awaitOrInterruptible() {
while (!Thread.interrupted()) {
if (awaitOrInterruptible(1, SECONDS)) {
return true;
}
}
return false;
}
/**
* @param timeoutMilliseconds
*
* @return True if the Future is done or False if interrupted
*/
@Override
public final boolean awaitOrInterruptible(final long timeoutMilliseconds) {
return awaitOrInterruptible(MILLISECONDS.toNanos(timeoutMilliseconds),
false);
}
/**
* @param timeout
* @param unit
*
* @return True if the Future is done or False if interrupted
*/
@Override
public final boolean awaitOrInterruptible(final long timeout,
final TimeUnit unit) {
return awaitOrInterruptible(unit.toNanos(timeout), false);
}
/**
* @param timeoutNanos
* @param interruptable
*
* @return True if the Future is done or False if interrupted
*/
private boolean awaitOrInterruptible(final long timeoutNanos,
final boolean interruptable) {
try {
if (await0(timeoutNanos, interruptable) && !Thread.interrupted()) {
return true;
}
} catch (final InterruptedException e) {//NOSONAR
SysErrLogger.FAKE_LOGGER.ignoreLog(e);
}
return false;
}
private boolean await0(final long timeoutNanos, final boolean interruptable)
throws InterruptedException {
if (done) {
return done;
}
if (timeoutNanos <= 0) {
return done;
}
if (interruptable && Thread.interrupted()) {
throw new InterruptedException();
}
final long startTime = System.nanoTime();
long waitTime = timeoutNanos;
boolean interrupted = false;
try {
for (; ; ) {
synchronized (this) {
if (done) {
return done;
}
waiters++;
try {
wait(waitTime / 1000000, (int) (waitTime % 1000000));
} catch (final InterruptedException e) {//NOSONAR
SysErrLogger.FAKE_LOGGER.ignoreLog(e);
if (interruptable) {
throw e;
} else {
interrupted = true;
}
} finally {
waiters--;
}
}
if (done) {
return true;
}
waitTime = timeoutNanos - (System.nanoTime() - startTime);
if (waitTime <= 0) {
return done;
}
}
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
}
/**
* Marks this future as a success and notifies all listeners.
*
* @return {@code true} if and only if successfully marked this future as a
* success. Otherwise {@code false}
* because this future is already marked as either a success or a
* failure.
*/
@Override
public final boolean setSuccess() {
synchronized (this) {
// Allow only once.
if (done) {
return false;
}
done = true;
if (waiters > 0) {
notifyAll();
}
}
return true;
}
/**
* Marks this future as a failure and notifies all listeners.
*
* @param cause
*
* @return {@code true} if and only if successfully marked this future as a
* failure. Otherwise {@code false}
* because this future is already marked as either a success or a
* failure.
*/
@Override
public final boolean setFailure(final Throwable cause) {
synchronized (this) {
// Allow only once.
if (done) {
return false;
}
this.cause = cause;
done = true;
if (waiters > 0) {
notifyAll();
}
}
return true;
}
/**
* Cancels the operation associated with this future and notifies all
* listeners if canceled successfully.
*
* @return {@code true} if and only if the operation has been canceled.
* {@code
* false} if the operation can't
* be canceled or is already completed.
*/
@Override
public final boolean cancel() {
if (!cancellable) {
return false;
}
synchronized (this) {
// Allow only once.
if (done) {
return false;
}
cause = CANCELLED;
done = true;
if (waiters > 0) {
notifyAll();
}
}
return true;
}
/**
* Experimental: try to re-enable the future
*/
@Override
public final void reset() {
synchronized (this) {
done = false;
cause = null;
}
}
}