Give EventDispatcher more predictable behavior

If EventDispatcher.removeListener is called to remove a listener,
and if the call is made from the same thread that said listener
handles events on, then it should be guaranteed that the listener
will not be subsequently invoked on that thread.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=218331427
This commit is contained in:
olly 2018-10-23 06:23:50 -07:00 committed by Oliver Woodman
parent 5ae60a6f18
commit 13e0513ea3

View File

@ -39,22 +39,23 @@ public final class EventDispatcher<T> {
/** The list of listeners and handlers. */
private final CopyOnWriteArrayList<HandlerAndListener<T>> listeners;
/** Creates event dispatcher. */
/** Creates an event dispatcher. */
public EventDispatcher() {
listeners = new CopyOnWriteArrayList<>();
}
/** Adds listener to event dispatcher. */
/** Adds a listener to the event dispatcher. */
public void addListener(Handler handler, T eventListener) {
Assertions.checkArgument(handler != null && eventListener != null);
removeListener(eventListener);
listeners.add(new HandlerAndListener<>(handler, eventListener));
}
/** Removes listener from event dispatcher. */
/** Removes a listener from the event dispatcher. */
public void removeListener(T eventListener) {
for (HandlerAndListener<T> handlerAndListener : listeners) {
if (handlerAndListener.listener == eventListener) {
handlerAndListener.release();
listeners.remove(handlerAndListener);
}
}
@ -67,19 +68,33 @@ public final class EventDispatcher<T> {
*/
public void dispatch(Event<T> event) {
for (HandlerAndListener<T> handlerAndListener : listeners) {
T eventListener = handlerAndListener.listener;
handlerAndListener.handler.post(() -> event.sendTo(eventListener));
handlerAndListener.dispatch(event);
}
}
private static final class HandlerAndListener<T> {
public final Handler handler;
public final T listener;
private final Handler handler;
private final T listener;
private boolean released;
public HandlerAndListener(Handler handler, T eventListener) {
this.handler = handler;
this.listener = eventListener;
}
public void release() {
released = true;
}
public void dispatch(Event<T> event) {
handler.post(
() -> {
if (!released) {
event.sendTo(listener);
}
});
}
}
}