package org.limewire.mojito.concurrent;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.limewire.concurrent.AsyncFutureTask;
import org.limewire.concurrent.ExecutorsHelper;
import org.limewire.concurrent.FutureEvent;
import org.limewire.listener.EventListener;
import org.limewire.mojito.Context;
import org.limewire.mojito.util.EventUtils;
/**
* {@link DHTFutureTask}s have a built-in watchdog {@link Thread} that
* interrupts all waiting {@link Thread}s after a predefined period of
* time.
*
* @see AsyncFutureTask
*/
public class DHTFutureTask<V> extends AsyncFutureTask<V> implements DHTFuture<V> {
private static final ScheduledExecutorService WATCHDOG
= Executors.newSingleThreadScheduledExecutor(
ExecutorsHelper.defaultThreadFactory("WatchdogThread"));
private final Context context;
private final DHTTask<V> task;
private final long timeout;
private final TimeUnit unit;
private ScheduledFuture<?> watchdog;
private boolean wasTimeout = false;
/**
* Creates an {@link DHTFutureTask}
*/
public DHTFutureTask(Context context, DHTTask<V> task) {
this.context = context;
this.task = task;
this.timeout = task.getWaitOnLockTimeout();
this.unit = TimeUnit.MILLISECONDS;
}
public Context getContext() {
return context;
}
@Override
protected synchronized void doRun() {
if (!isDone()) {
watchdog();
start();
}
}
/**
* Starts the {@link DHTTask}
*/
protected synchronized void start() {
task.start(this);
}
/**
* Starts the watchdog task
*/
private synchronized boolean watchdog() {
if (timeout == -1L || isDone()) {
return false;
}
Runnable task = new Runnable() {
@Override
public void run() {
synchronized (DHTFutureTask.this) {
if (!isDone()) {
wasTimeout = true;
handleTimeout(timeout, unit);
}
}
}
};
watchdog = WATCHDOG.schedule(task, timeout, unit);
return true;
}
/**
* Called in case of a timeout. The default implmenetation
* calls {@link #setException(Throwable)} with a {@link TimeoutException}
* as an argument.
*/
protected void handleTimeout(long timeout, TimeUnit unit) {
setException(new TimeoutException(timeout + " " + unit));
}
@Override
public long getTimeout(TimeUnit unit) {
return unit.convert(timeout, this.unit);
}
@Override
public long getTimeoutInMillis() {
return getTimeout(TimeUnit.MILLISECONDS);
}
@Override
public synchronized boolean isTimeout() {
return wasTimeout;
}
/**
* Overridden and declared final to do some critical bookkeeping.
* Please override {@link #done0()} in custom implementations.
*/
@Override
protected final void done() {
synchronized (this) {
// Cancel the watchdog
if (watchdog != null) {
watchdog.cancel(true);
}
}
done0();
}
/**
* Protected method that is invoked when this {@link DHTFutureTask}
* completes. You may override this method.
*
* @see #done()
*/
protected void done0() {
// Override
}
@Override
protected boolean isEventThread() {
return EventUtils.isEventThread();
}
@Override
protected void fireOperationComplete(
final EventListener<FutureEvent<V>>[] listeners,
final FutureEvent<V> event) {
Runnable task = new Runnable() {
@Override
public void run() {
DHTFutureTask.super.fireOperationComplete(listeners, event);
}
};
EventUtils.fireEvent(task);
}
}