Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 37a0f188b70b3ad4295c13a3e9b273fee758dd68 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*******************************************************************************
 * 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);
		}
	}
}

Back to the top