/**
* This file is part of Waarp Project.
*
* Copyright 2009, Frederic Bregier, 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 static java.util.concurrent.TimeUnit.MILLISECONDS;
import java.util.concurrent.TimeUnit;
/**
* Ftp Future operation<br>
* Completely inspired from the excellent ChannelFuture of Netty, but without any channel inside.
*
* @author Frederic Bregier
*
*/
public class WaarpFuture {
private static final Throwable CANCELLED = new Throwable();
private final boolean cancellable;
private 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(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
*/
public synchronized boolean isDone() {
return done;
}
/**
* Returns {@code true} if and only if the operation was completed successfully.
*
* @return True if the future is successful
*/
public synchronized 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
*/
public synchronized boolean isFailed() {
return 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.
*/
public synchronized 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
*/
public synchronized boolean isCancelled() {
return cause == CANCELLED;
}
/**
* Rethrows the exception that caused this future fail if this future is complete and failed.
*/
public WaarpFuture rethrowIfFailed() throws Exception {
if (!isDone()) {
return this;
}
Throwable cause = getCause();
if (cause == null) {
return this;
}
if (cause instanceof Exception) {
throw (Exception) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(cause);
}
/**
* Waits for this future to be completed.
*
* @return The WaarpFuture
*
* @throws InterruptedException
* if the current thread was interrupted
*/
public WaarpFuture await() throws InterruptedException {
if (Thread.interrupted()) {
throw new InterruptedException();
}
synchronized (this) {
while (!done) {
waiters++;
try {
this.wait();
} finally {
waiters--;
}
}
}
return this;
}
/**
* Waits for this future to be completed within the specified time limit.
*
* @param timeout
* @param unit
*
* @return {@code true} if and only if the future was completed within the specified time limit
*
* @throws InterruptedException
* if the current thread was interrupted
*/
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return await0(unit.toNanos(timeout), true);
}
/**
* Waits for this future to be completed within the specified time limit.
*
* @param timeoutMillis
*
* @return {@code true} if and only if the future was completed within the specified time limit
*
* @throws InterruptedException
* if the current thread was interrupted
*/
public boolean await(long timeoutMillis) throws InterruptedException {
return await0(MILLISECONDS.toNanos(timeoutMillis), true);
}
/**
* Waits for this future to be completed without interruption. This method catches an {@link InterruptedException} and
* discards it silently.
*
* @return The WaarpFuture
*/
public WaarpFuture awaitUninterruptibly() {
boolean interrupted = false;
synchronized (this) {
while (!done) {
waiters++;
try {
this.wait();
} catch (InterruptedException e) {
interrupted = true;
} finally {
waiters--;
}
}
}
if (interrupted) {
Thread.currentThread().interrupt();
}
return this;
}
/**
* Waits for this future to be completed within the specified time limit without interruption.
* This method catches an {@link InterruptedException} and discards it silently.
*
* @param timeout
* @param unit
*
* @return {@code true} if and only if the future was completed within the specified time limit
*/
public boolean awaitUninterruptibly(long timeout, TimeUnit unit) {
try {
return await0(unit.toNanos(timeout), false);
} catch (InterruptedException e) {
throw new InternalError();
}
}
/**
* Waits for this future to be completed within the specified time limit without interruption.
* This method catches an {@link InterruptedException} and discards it silently.
*
* @param timeoutMillis
*
* @return {@code true} if and only if the future was completed within the specified time limit
*/
public boolean awaitUninterruptibly(long timeoutMillis) {
try {
return await0(MILLISECONDS.toNanos(timeoutMillis), false);
} catch (InterruptedException e) {
throw new InternalError();
}
}
private boolean await0(long timeoutNanos, boolean interruptable)
throws InterruptedException {
if (interruptable && Thread.interrupted()) {
throw new InterruptedException();
}
long startTime = timeoutNanos <= 0 ? 0 : System.nanoTime();
long waitTime = timeoutNanos;
boolean interrupted = false;
try {
synchronized (this) {
if (done) {
return done;
} else if (waitTime <= 0) {
return done;
}
waiters++;
try {
for (;;) {
try {
this.wait(waitTime / 1000000,
(int) (waitTime % 1000000));
} catch (InterruptedException e) {
if (interruptable) {
throw e;
} else {
interrupted = true;
}
}
if (done) {
return true;
}
waitTime = timeoutNanos - (System.nanoTime() - startTime);
if (waitTime <= 0) {
return done;
}
}
} finally {
waiters--;
}
}
} 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.
*/
public 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.
*/
public boolean setFailure(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.
*/
public 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
*/
public void reset() {
synchronized (this) {
this.done = false;
this.cause = null;
}
}
}