package org.limewire.concurrent; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.FutureTask; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.limewire.listener.EventListener; import org.limewire.listener.EventListenerList; import org.limewire.listener.EventListenerList.EventListenerListContext; /** * A FutureTask that can notify listeners of results. The preferred way of * using a {@link ListeningFuture} is by using a * {@link ListeningExecutorService} or {@link ScheduledListeningExecutorService}, * in which case the <code>submit</code> methods and one-time <code>schedule</code> * methods will returning a {@link ListeningFuture} or a {@link ScheduledListeningFuture}. * * However, if you are using a standard {@link ExecutorService}, you can create * a {@link ListeningFuture} by the following: * <pre> * ListeningRunnableFuture future = new ListeningFutureTask(callable); * executorService.execute(future); * return future; * </pre> * Whereas previously you would have done this: * <pre> * return executorService.submit(callable); * </pre> */ public class ListeningFutureTask<V> extends FutureTask<V> implements RunnableListeningFuture<V> { private final AtomicReference<EventListenerList<FutureEvent<V>>> listenersRef = new AtomicReference<EventListenerList<FutureEvent<V>>>(new EventListenerList<FutureEvent<V>>()); // The listenerContext is required to make sure that listeners are notified in the correct // threads. We eagerly clear the listenerRef to release old listeners, but need to keep // the context around to make sure future listeners reuse the context. private final EventListenerListContext listenerContext = listenersRef.get().getContext(); public ListeningFutureTask(Callable<V> callable) { super(callable); } public ListeningFutureTask(Runnable runnable, V result) { super(runnable, result); } @Override protected void done() { EventListenerList<FutureEvent<V>> listeners = listenersRef.getAndSet(null); assert listeners != null; if (listeners.size() > 0) { listeners.broadcast(FutureEvent.createEvent(this)); } } @Override public void addFutureListener(EventListener<FutureEvent<V>> listener) { boolean added = false; EventListenerList<FutureEvent<V>> listeners = listenersRef.get(); // Add the listener & set it back -- we add a proxy listener // because there's a chance that we add it to the list // before another thread sets it to null, leaving us // to potentially call methods on the listener twice. // (Once from the done() thread, and once from this thread.) if (!isDone() && listeners != null) { listeners.addListener(new ProxyListener<V>(listener, listenerContext)); added = listenersRef.compareAndSet(listeners, listeners); } if (!added) { EventListenerList.dispatch(listener, FutureEvent.createEvent(this), listenerContext); } } private static class ProxyListener<V> implements EventListener<FutureEvent<V>> { private final AtomicBoolean called = new AtomicBoolean(false); private final EventListenerListContext listenerContext; private final EventListener<FutureEvent<V>> delegate; public ProxyListener(EventListener<FutureEvent<V>> delegate, EventListenerListContext listenerContext) { this.delegate = delegate; this.listenerContext = listenerContext; } @Override public void handleEvent(FutureEvent<V> event) { if (!called.getAndSet(true)) { // Dispatch via EventListenerList to support annotations. EventListenerList.dispatch(delegate, event, listenerContext); } } } }