Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: d60303492e6575e95119088a1bab603bb44e9138 (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
/*******************************************************************************
 * Copyright (c) 2003, 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.osgi.framework.eventmgr;

import java.util.ArrayList;

/**
 * The ListenerQueue is used to snapshot the set 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
 * using asynchronous delivery. No delivery order is guaranteed for synchronous
 * delivery to avoid any potential deadly embraces.
 *
 * <p>ListenerQueue objects are created as necesssary to build a set of listeners
 * that should receive a specific event or events. Once the set is created, the event
 * can then be synchronously or asynchronously delivered to the set 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 memory cost of a ListenerQueue object is
 * low since the ListenerQueue object shares the array of listeners with the EventListeners 
 * object which are queued.
 * EventListeners uses copy-on-write semantics for managing the array and will copy the array
 * before changing it once the array has been shared with a ListenerQueue. This minimizes 
 * object creation while guaranteeing the snapshot list is never modified once created.
 */

public class ListenerQueue {
	/**
	 * EventManager with which this queue is associated.
	 */
	protected final EventManager manager;
	/**
	 * A list of listener lists.
	 */
	private final ArrayList 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 snapshop 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 ArrayList();
		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.
	 */
	public synchronized void queueListeners(EventListeners listeners, EventDispatcher dispatcher) {
		if (readOnly) {
			throw new IllegalStateException();
		}

		if (listeners != null) {
			ListElement[] list = listeners.getListeners();

			if (list.length > 0) {
				queue.add(new ListElement(list, 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, Object eventObject) {
		synchronized (this) {
			readOnly = true;
		}
		EventThread eventThread = manager.getEventThread();
		synchronized (eventThread) { /* synchronize on the EventThread to ensure no interleaving of posting to the event thread */
			int size = queue.size();
			for (int i = 0; i < size; i++) { /* iterate over the list of listener lists */
				ListElement list = (ListElement)queue.get(i);
				eventThread.postEvent((ListElement[]) list.primary, (EventDispatcher) list.companion, 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, Object eventObject) {
		synchronized (this) {
			readOnly = true;
		}
		// We can't guarantee any delivery order for synchronous events.
		// Attempts to do so result in deadly embraces.
		int size = queue.size();
		for (int i = 0; i < size; i++) { /* iterate over the list of listener lists */
			ListElement list = (ListElement)queue.get(i);
			EventManager.dispatchEvent((ListElement[]) list.primary, (EventDispatcher) list.companion, eventAction, eventObject);
		}
	}
}

Back to the top