package org.limewire.listener;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import org.limewire.util.ExceptionUtils;
public class PendingEventMulticasterImpl<E> implements EventMulticaster<E>, PendingEventBroadcaster<E> {
private final EventMulticaster<E> multicaster;
private final ConcurrentLinkedQueue<E> queuedEvents = new ConcurrentLinkedQueue<E>();
private final AtomicBoolean firing = new AtomicBoolean();
public PendingEventMulticasterImpl() {
this(new EventMulticasterImpl<E>());
}
public PendingEventMulticasterImpl(EventMulticaster<E> multicaster) {
this.multicaster = multicaster;
}
@Override
public void addListener(EventListener<E> eventListener) {
multicaster.addListener(eventListener);
}
@Override
public boolean removeListener(EventListener<E> eventListener) {
return multicaster.removeListener(eventListener);
}
@Override
public void handleEvent(E event) {
broadcast(event);
}
@Override
public void broadcast(E event) {
addPendingEvent(event);
firePendingEvents();
}
@Override
public void addPendingEvent(E event) {
queuedEvents.add(event);
}
@Override
public void firePendingEvents() {
// It's possible that an event
// was queued and another thread called firePendingEvents
// before after we finished polling queuedEvents
// but before firing was set back to false. To allow
// the queued event to fire, we loop and exit if
// someone else is firing. This guarantees that atleast
// one thread will actively be sending queued events until
// no queued events remain.
while (!queuedEvents.isEmpty()) {
if (firing.compareAndSet(false, true)) {
try {
Throwable t = null;
E e;
while ((e = queuedEvents.poll()) != null) {
try {
multicaster.broadcast(e);
} catch(Throwable thrown) {
thrown = ExceptionUtils.reportOrReturn(thrown);
if(thrown != null && t == null) {
t = thrown;
}
}
}
if(t != null) {
ExceptionUtils.reportOrRethrow(t);
}
} finally {
firing.set(false);
}
} else {
// Exit while loop, allow firing thread
// to take control of broadcasting the pending events.
break;
}
}
}
@Override
public EventListenerList.EventListenerListContext getListenerContext() {
return multicaster.getListenerContext();
}
}