blob: 37a0f188b70b3ad4295c13a3e9b273fee758dd68 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2016 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.framework.eventmgr;
import java.util.Map;
import java.util.Set;
import org.eclipse.osgi.framework.eventmgr.EventManager.EventThread;
/**
* The ListenerQueue is used to snapshot the list of listeners at the time the event
* is fired. The snapshot list is then used to dispatch
* events to those listeners. A ListenerQueue object is associated with a
* specific EventManager object. ListenerQueue objects constructed with the same
* EventManager object will get in-order delivery of events when
* using asynchronous delivery. No delivery order is guaranteed for synchronous
* delivery to avoid any potential deadly embraces.
*
* <p>ListenerQueue objects are created as necessary to build a list of listeners
* that should receive a specific event or events. Once the list is created, the event
* can then be synchronously or asynchronously delivered to the list of
* listeners. After the event has been dispatched for delivery, the
* ListenerQueue object should be discarded as it is likely the list of listeners is stale.
* A new ListenerQueue object should be created when it is time to deliver
* another event. The Sets used to build the list of listeners must not change after being
* added to the list.
* @since 3.1
*/
public class ListenerQueue<K, V, E> {
/**
* EventManager with which this queue is associated.
*/
protected final EventManager manager;
/**
* A list of listener lists.
*/
private final Map<Set<Map.Entry<K, V>>, EventDispatcher<K, V, E>> queue;
/**
* Once the listener queue has been used to dispatch an event,
* you cannot add modify the queue.
* Access to this field must be protected by a synchronized region.
*/
private boolean readOnly;
/**
* ListenerQueue constructor. This method creates an empty snapshot list.
*
* @param manager The EventManager this queue is associated with.
* @throws IllegalArgumentException If manager is null.
*/
public ListenerQueue(EventManager manager) {
if (manager == null) {
throw new IllegalArgumentException();
}
this.manager = manager;
queue = new CopyOnWriteIdentityMap<>();
readOnly = false;
}
/**
* Add a listener list to the snapshot list. This method can be called multiple times, prior to
* calling one of the dispatchEvent methods, to build the set of listeners for the
* delivery of a specific event. The current list of listeners in the specified EventListeners
* object is added to the snapshot list.
*
* @param listeners An EventListeners object to add to the queue. The current listeners
* in the EventListeners object will be called when an event is dispatched.
* @param dispatcher An EventDispatcher object to use when dispatching an event
* to the listeners on the specified EventListeners.
* @throws IllegalStateException If called after one of the dispatch methods has been called.
* @deprecated As of 3.5. Replaced by {@link #queueListeners(Set, EventDispatcher)}.
*/
public void queueListeners(EventListeners<K, V> listeners, EventDispatcher<K, V, E> dispatcher) {
queueListeners(listeners.entrySet(), dispatcher);
}
/**
* Add a set of listeners to the snapshot list. This method can be called multiple times, prior to
* calling one of the dispatchEvent methods, to build the list of listeners for the
* delivery of a specific event. The specified listeners
* are added to the snapshot list.
*
* @param listeners A Set of Map.Entries to add to the queue. This is typically the entrySet
* from a CopyOnWriteIdentityMap object. This set must not change after being added to this
* snapshot list.
* @param dispatcher An EventDispatcher object to use when dispatching an event
* to the specified listeners.
* @throws IllegalStateException If called after one of the dispatch methods has been called.
* @since 3.5
*/
public synchronized void queueListeners(Set<Map.Entry<K, V>> listeners, EventDispatcher<K, V, E> dispatcher) {
if (readOnly) {
throw new IllegalStateException();
}
if (!listeners.isEmpty()) {
queue.put(listeners, dispatcher); // enqueue the list and its dispatcher
}
}
/**
* Asynchronously dispatch an event to the snapshot list. An event dispatch thread
* maintained by the associated EventManager is used to deliver the events.
* This method may return immediately to the caller.
*
* @param eventAction This value is passed to the EventDispatcher.
* @param eventObject This object is passed to the EventDispatcher.
*/
public void dispatchEventAsynchronous(int eventAction, E eventObject) {
synchronized (this) {
readOnly = true;
}
EventThread<K, V, E> eventThread = manager.getEventThread();
synchronized (eventThread) { /* synchronize on the EventThread to ensure no interleaving of posting to the event thread */
for (Map.Entry<Set<Map.Entry<K, V>>, EventDispatcher<K, V, E>> entry : queue.entrySet()) { /* iterate over the list of listener lists */
eventThread.postEvent(entry.getKey(), entry.getValue(), eventAction, eventObject);
}
}
}
/**
* Synchronously dispatch an event to the snapshot list. The event may
* be dispatched on the current thread or an event dispatch thread
* maintained by the associated EventManager.
* This method will not return to the caller until the EventDispatcher
* has been called (and has returned) for each listener on the queue.
*
* @param eventAction This value is passed to the EventDispatcher.
* @param eventObject This object is passed to the EventDispatcher.
*/
public void dispatchEventSynchronous(int eventAction, E eventObject) {
synchronized (this) {
readOnly = true;
}
// We can't guarantee any delivery order for synchronous events.
// Attempts to do so result in deadly embraces.
for (Map.Entry<Set<Map.Entry<K, V>>, EventDispatcher<K, V, E>> entry : queue.entrySet()) { /* iterate over the list of listener lists */
EventManager.dispatchEvent(entry.getKey(), entry.getValue(), eventAction, eventObject);
}
}
}