tmf: Add asynchronous way of broadcasting signals
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.core / src / org / eclipse / linuxtools / tmf / core / signal / TmfSignalManager.java
CommitLineData
8c8bf09f 1/*******************************************************************************
d91063d0 2 * Copyright (c) 2009, 2014 Ericsson
6256d8ad 3 *
8c8bf09f
ASL
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
6256d8ad 8 *
8c8bf09f
ASL
9 * Contributors:
10 * Francois Chouinard - Initial API and implementation
fec1ac0b 11 * Bernd Hufmann - Update register methods
8c8bf09f
ASL
12 *******************************************************************************/
13
6c13869b 14package org.eclipse.linuxtools.tmf.core.signal;
8c8bf09f
ASL
15
16import java.lang.reflect.InvocationTargetException;
17import java.lang.reflect.Method;
18import java.util.ArrayList;
19import java.util.HashMap;
20import java.util.List;
21import java.util.Map;
d91063d0
BH
22import java.util.concurrent.ExecutorService;
23import java.util.concurrent.Executors;
8c8bf09f 24
8555a0f3 25import org.eclipse.linuxtools.internal.tmf.core.Activator;
5500a7f0 26import org.eclipse.linuxtools.internal.tmf.core.TmfCoreTracer;
dc299841 27
8c8bf09f 28/**
8d2e2848
FC
29 * This class manages the set of signal listeners and the signals they are
30 * interested in. When a signal is broadcasted, the appropriate listeners
31 * signal handlers are invoked.
6256d8ad 32 *
4b7b3670
FC
33 * @version 1.0
34 * @author Francois Chouinard
4f8ca6a1 35 */
8c8bf09f
ASL
36public class TmfSignalManager {
37
d9ec9800
AM
38 // The set of event listeners and their corresponding handler methods.
39 // Note: listeners could be restricted to ITmfComponents but there is no
40 // harm in letting anyone use this since it is not tied to anything but
41 // the signal data type.
a4524c1b
AM
42 private static Map<Object, Method[]> fListeners = new HashMap<>();
43 private static Map<Object, Method[]> fVIPListeners = new HashMap<>();
d9ec9800 44
d91063d0
BH
45 // The signal executor for asynchronous signals
46 private static final ExecutorService fExecutor = Executors.newSingleThreadExecutor();
47
d9ec9800
AM
48 // If requested, add universal signal tracer
49 // TODO: Temporary solution: should be enabled/disabled dynamically
50 private static boolean fTraceIsActive = false;
51 private static TmfSignalTracer fSignalTracer;
52
53 static {
54 if (fTraceIsActive) {
55 fSignalTracer = TmfSignalTracer.getInstance();
56 register(fSignalTracer);
57 }
58 }
8c8bf09f 59
4f8ca6a1
AM
60 /**
61 * Register an object to the signal manager. This object can then implement
62 * handler methods, marked with @TmfSignalHandler and with the expected
63 * signal type as parameter.
64 *
65 * @param listener
66 * The object that will be notified of new signals
67 */
68 public static synchronized void register(Object listener) {
fec1ac0b 69 deregister(listener); // make sure that listener is only registered once
4f8ca6a1
AM
70 Method[] methods = getSignalHandlerMethods(listener);
71 if (methods.length > 0) {
72 fListeners.put(listener, methods);
73 }
74 }
8c8bf09f 75
4f8ca6a1
AM
76 /**
77 * Register an object to the signal manager as a "VIP" listener. All VIP
78 * listeners will all receive the signal before the manager moves on to the
79 * lowly, non-VIP listeners.
80 *
81 * @param listener
82 * The object that will be notified of new signals
83 */
d5c13688 84 public static synchronized void registerVIP(Object listener) {
fec1ac0b 85 deregister(listener); // make sure that listener is only registered once
d5c13688 86 Method[] methods = getSignalHandlerMethods(listener);
6256d8ad 87 if (methods.length > 0) {
d5c13688 88 fVIPListeners.put(listener, methods);
6256d8ad 89 }
d5c13688
FC
90 }
91
4f8ca6a1
AM
92 /**
93 * De-register a listener object from the signal manager. This means that
94 * its @TmfSignalHandler methods will no longer be called.
95 *
96 * @param listener
97 * The object to de-register
98 */
99 public static synchronized void deregister(Object listener) {
100 fVIPListeners.remove(listener);
d5c13688 101 fListeners.remove(listener);
4f8ca6a1 102 }
8c8bf09f 103
d9ec9800
AM
104 /**
105 * Returns the list of signal handlers in the listener. Signal handler name
106 * is irrelevant; only the annotation (@TmfSignalHandler) is important.
107 *
108 * @param listener
109 * @return
110 */
111 private static Method[] getSignalHandlerMethods(Object listener) {
a4524c1b 112 List<Method> handlers = new ArrayList<>();
d9ec9800
AM
113 Method[] methods = listener.getClass().getMethods();
114 for (Method method : methods) {
115 if (method.isAnnotationPresent(TmfSignalHandler.class)) {
116 handlers.add(method);
117 }
118 }
119 return handlers.toArray(new Method[handlers.size()]);
120 }
121
122 static int fSignalId = 0;
123
124 /**
d91063d0
BH
125 * Invokes the handling methods that listens to signals of a given type in
126 * the current thread.
d9ec9800
AM
127 *
128 * The list of handlers is built on-the-fly to allow for the dynamic
129 * creation/deletion of signal handlers. Since the number of signal handlers
130 * shouldn't be too high, this is not a big performance issue to pay for the
131 * flexibility.
132 *
133 * For synchronization purposes, the signal is bracketed by two synch
134 * signals.
135 *
136 * @param signal
137 * the signal to dispatch
138 */
139 public static synchronized void dispatchSignal(TmfSignal signal) {
140 int signalId = fSignalId++;
141 sendSignal(new TmfStartSynchSignal(signalId));
142 signal.setReference(signalId);
143 sendSignal(signal);
144 sendSignal(new TmfEndSynchSignal(signalId));
145 }
146
d91063d0
BH
147 /**
148 * Invokes the handling methods that listens to signals of a given type
149 * in a separate thread which will call
150 * {@link TmfSignalManager#dispatchSignal(TmfSignal)}.
151 *
152 * If a signal is already processed the signal will be queued and
153 * dispatched after the ongoing signal finishes.
154 *
155 * @param signal
156 * the signal to dispatch
157 * @since 3.0
158 */
159 public static void dispatchSignalAsync(final TmfSignal signal) {
160 if (!fExecutor.isShutdown()) {
161 fExecutor.execute(new Runnable() {
162 @Override
163 public void run() {
164 dispatchSignal(signal);
165 }
166 });
167 }
168 }
169
170 /**
171 * Disposes the signal manager
172 * @since 3.0
173 */
174 public static void dispose() {
175 fExecutor.shutdown();
176 }
177
d9ec9800 178 private static void sendSignal(TmfSignal signal) {
d5c13688
FC
179 sendSignal(fVIPListeners, signal);
180 sendSignal(fListeners, signal);
181 }
182
d9ec9800 183 private static void sendSignal(Map<Object, Method[]> listeners, TmfSignal signal) {
d5c13688 184
5500a7f0
FC
185 if (TmfCoreTracer.isSignalTraced()) {
186 TmfCoreTracer.traceSignal(signal, "(start)"); //$NON-NLS-1$
6256d8ad 187 }
d5c13688
FC
188
189 // Build the list of listener methods that are registered for this signal
190 Class<?> signalClass = signal.getClass();
a4524c1b 191 Map<Object, List<Method>> targets = new HashMap<>();
d5c13688
FC
192 targets.clear();
193 for (Map.Entry<Object, Method[]> entry : listeners.entrySet()) {
a4524c1b 194 List<Method> matchingMethods = new ArrayList<>();
d5c13688
FC
195 for (Method method : entry.getValue()) {
196 if (method.getParameterTypes()[0].isAssignableFrom(signalClass)) {
197 matchingMethods.add(method);
198 }
199 }
200 if (!matchingMethods.isEmpty()) {
201 targets.put(entry.getKey(), matchingMethods);
202 }
203 }
204
6256d8ad 205 // Call the signal handlers
d5c13688
FC
206 for (Map.Entry<Object, List<Method>> entry : targets.entrySet()) {
207 for (Method method : entry.getValue()) {
208 try {
209 method.invoke(entry.getKey(), new Object[] { signal });
5500a7f0 210 if (TmfCoreTracer.isSignalTraced()) {
d5c13688
FC
211 Object key = entry.getKey();
212 String hash = String.format("%1$08X", entry.getKey().hashCode()); //$NON-NLS-1$
213 String target = "[" + hash + "] " + key.getClass().getSimpleName() + ":" + method.getName(); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
5500a7f0 214 TmfCoreTracer.traceSignal(signal, target);
d5c13688
FC
215 }
216 } catch (IllegalArgumentException e) {
5500a7f0 217 Activator.logError("Exception handling signal " + signal + " in method " + method, e); //$NON-NLS-1$ //$NON-NLS-2$
d5c13688 218 } catch (IllegalAccessException e) {
5500a7f0 219 Activator.logError("Exception handling signal " + signal + " in method " + method, e); //$NON-NLS-1$ //$NON-NLS-2$
d5c13688 220 } catch (InvocationTargetException e) {
5500a7f0 221 Activator.logError("Exception handling signal " + signal + " in method " + method, e); //$NON-NLS-1$ //$NON-NLS-2$
d5c13688
FC
222 }
223 }
224 }
225
5500a7f0
FC
226 if (TmfCoreTracer.isSignalTraced()) {
227 TmfCoreTracer.traceSignal(signal, "(end)"); //$NON-NLS-1$
6256d8ad 228 }
d5c13688 229 }
dc299841 230
dc299841 231}
This page took 0.058239 seconds and 5 git commands to generate.