package com.linkedin.parseq;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import com.linkedin.parseq.Cancellable;
/**
* A wrapper around a delayed executor that provides better behavior for
* cancellation. If a task is cancelled (or run), we forget the reference to
* that task. Without this indirection it is possible that the underlying
* scheduled executor will hold a reference to the task - even if it has been
* cancelled - thus keeping a path to the task from a GC root.
*
* @author Chris Pettitt
*/
/* package private */ class IndirectDelayedExecutor implements DelayedExecutor {
private final DelayedExecutor _executor;
public IndirectDelayedExecutor(final DelayedExecutor executor) {
_executor = executor;
}
@Override
public Cancellable schedule(final long delay, final TimeUnit unit, final Runnable command) {
final IndirectRunnable indirectRunnable = new IndirectRunnable(command);
final Cancellable cancellable = _executor.schedule(delay, unit, indirectRunnable);
return new IndirectCancellable(cancellable, indirectRunnable);
}
private static class IndirectRunnable implements Runnable {
private AtomicReference<Runnable> _commandRef;
public IndirectRunnable(final Runnable command) {
_commandRef = new AtomicReference<Runnable>(command);
}
@Override
public void run() {
final Runnable runnable = _commandRef.get();
if (runnable != null && _commandRef.compareAndSet(runnable, null)) {
runnable.run();
}
}
public boolean cancel() {
final Runnable runnable = _commandRef.get();
return (runnable != null && _commandRef.compareAndSet(runnable, null));
}
}
private static class IndirectCancellable implements Cancellable {
private final Cancellable _cancellable;
private final IndirectRunnable _runnable;
private IndirectCancellable(final Cancellable cancellable, final IndirectRunnable runnable) {
_cancellable = cancellable;
_runnable = runnable;
}
@Override
public boolean cancel(final Exception reason) {
return _runnable.cancel() && _cancellable.cancel(reason);
}
}
}