1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
14 * @author Toni Kalajainen
16 package org.simantics.utils.threads;
18 import java.lang.reflect.InvocationTargetException;
19 import java.lang.reflect.Method;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.HashMap;
23 import java.util.List;
25 import java.util.Map.Entry;
27 import org.simantics.utils.threads.internal.ListenerList;
31 * @author Toni Kalajainen
33 * @see ListenerList Simple listener list
37 public class SyncListenerList<T> {
40 Map<IThreadWorkQueue, ListenerList<T>> lists =
41 new HashMap<IThreadWorkQueue, ListenerList<T>>(2);
43 /** Snapshot version */
44 Map<IThreadWorkQueue, T[]> snapshot;
46 Boolean requiresThreadSwitching = Boolean.FALSE;
51 private final Class<T> componentType;
53 @SuppressWarnings("unchecked")
54 public SyncListenerList(Class<?> componentType)
56 this.componentType = (Class<T>) componentType;
60 * Add listener to the list
61 * @param thread thread to use to handle the event
64 public synchronized void add(IThreadWorkQueue thread, T listener)
67 throw new IllegalArgumentException("null");
70 ListenerList<T> list = lists.get(thread);
72 list = new ListenerList<T>(componentType);
73 lists.put(thread, list);
76 if (thread!=CurrentThread.getThreadAccess())
77 requiresThreadSwitching = Boolean.TRUE;
81 * Contains elements that require thread switching.
82 * @return true if contains elements with thread access other than current thread
84 public synchronized Boolean containsContextSwitchingListeners() {
85 if (requiresThreadSwitching==null)
86 requiresThreadSwitching = _HasContextSwitching();
87 return requiresThreadSwitching;
92 * @return true if contains listeners that require context switching
94 private boolean _HasContextSwitching()
96 if (lists.size()!=0) return false;
97 if (lists.size()>1) return false;
98 Entry<IThreadWorkQueue, ListenerList<T>> e = lists.entrySet().iterator().next();
99 if (e.getKey()!=CurrentThread.getThreadAccess()) return false;
100 return !e.getValue().isEmpty();
104 * Add listener to the list. Listener will be invoked in the thread that
105 * happens to be running at the time of events.
109 public void add(T listener)
112 throw new IllegalArgumentException("null");
113 add(CurrentThread.getThreadAccess(), listener);
117 * Remove listener from the list
118 * @param thread thread to use to handle the event
121 public synchronized void remove(IThreadWorkQueue thread, T listener)
124 ListenerList<T> list = lists.get(thread);
125 if (list==null) return;
126 list.remove(listener);
128 lists.remove(thread);
130 requiresThreadSwitching = Boolean.FALSE;
133 if (requiresThreadSwitching==null) return;
134 if (!requiresThreadSwitching) return;
135 if (thread==CurrentThread.getThreadAccess()) return;
136 requiresThreadSwitching = null;
139 public void remove(T listener)
141 remove(CurrentThread.getThreadAccess(), listener);
144 public synchronized boolean isEmpty()
146 return lists.size()==0;
149 public synchronized void clear()
151 requiresThreadSwitching = Boolean.FALSE;
157 public synchronized Map<IThreadWorkQueue, T[]> getSnapshot()
159 if (snapshot==null) {
160 snapshot = new HashMap<IThreadWorkQueue, T[]>(lists.size());
161 for (Entry<IThreadWorkQueue, ListenerList<T>> e : lists.entrySet())
163 T[] list = e.getValue().getListeners();
164 snapshot.put(e.getKey(), list);
170 public void fireEventAsync(final Method m, final Object ... args)
173 throw new IllegalArgumentException("null");
174 if (isEmpty()) return;
175 Map<IThreadWorkQueue, T[]> snapshot = getSnapshot();
176 for (Entry<IThreadWorkQueue, T[]> e : snapshot.entrySet())
178 final IThreadWorkQueue thread = e.getKey();
179 final T[] list = e.getValue();
180 Runnable r = new Runnable() {
186 } catch (RuntimeException e) {
188 } catch (IllegalAccessException e) {
190 } catch (InvocationTargetException e) {
191 e.getCause().printStackTrace();
195 ThreadUtils.asyncExec(thread, r);
199 * Version that does not use ThreadUtils
200 public void fireEventSync(final Method m, final Object ... args)
202 Map<IThreadAccess, T[]> snapshot = getSnapshot();
203 final Semaphore s = new Semaphore(0);
205 int countAsyncThreads = 0;
207 // todo use snapshot version of lists
208 for (Entry<IThreadAccess, T[]> e : snapshot.entrySet())
210 final IThreadAccess thread = e.getKey();
211 if (!thread.currentThreadAccess()) {
216 // Start async prosessing
217 for (Entry<IThreadAccess, T[]> e : snapshot.entrySet())
219 final IThreadAccess thread = e.getKey();
220 if (thread.currentThreadAccess()) {
224 final T[] list = e.getValue();
225 Runnable r = new Runnable() {
233 } catch (RuntimeException e) {
235 } catch (IllegalAccessException e) {
237 } catch (InvocationTargetException e) {
247 ThreadUtils.asyncExec(thread, r);
249 // Start local thread processing
250 for (Entry<IThreadAccess, T[]> e : snapshot.entrySet())
252 final T[] list = e.getValue();
257 } catch (RuntimeException e1) {
258 e1.printStackTrace();
259 } catch (IllegalAccessException e2) {
260 e2.printStackTrace();
261 } catch (InvocationTargetException e3) {
262 e3.printStackTrace();
267 // wait until all threads are ready
269 s.acquire(countAsyncThreads);
270 } catch (InterruptedException e) {
275 public void addExecutables(Collection<Executable> container, final Method m, final Object ... args)
277 Map<IThreadWorkQueue, T[]> snapshot = getSnapshot();
279 for (Entry<IThreadWorkQueue, T[]> e : snapshot.entrySet())
281 IThreadWorkQueue thread = e.getKey();
282 final T[] list = e.getValue();
283 Runnable r = new Runnable() {
290 } catch (RuntimeException e) {
292 } catch (IllegalAccessException e) {
294 } catch (InvocationTargetException e) {
295 e.getCause().printStackTrace();
301 container.add( new Executable(thread, r) );
305 public Executable[] getExecutables(final Method m, final Object ... args)
307 Map<IThreadWorkQueue, T[]> snapshot = getSnapshot();
308 List<Executable> container =
309 new ArrayList<Executable>(snapshot.size());
310 addExecutables(container, m, args);
311 return container.toArray(new Executable[container.size()]);
314 // Version that uses thread utils
315 public void fireEventSync(final Method m, final Object ... args)
318 throw new IllegalArgumentException("null");
319 if (isEmpty()) return;
320 ThreadUtils.multiSyncExec(getExecutables(m, args));
323 public static Method getMethod(Class<?> clazz, String name)
326 Method result = null;
327 for (Method m : clazz.getMethods())
329 if (!m.getName().equals(name)) continue;
333 if (count!=1) throw new Error("Unexpected method \""+name+"\" count in class "+clazz.getName());
337 public synchronized T[] getListenersByThread(IThreadWorkQueue ta)
339 ListenerList<T> l = lists.get(ta);
340 if (l==null) return null;
341 return l.getListeners();
345 * Is the list executable in current thread.
347 * @return <code>true</code> if executable
349 public synchronized boolean executableInCurrentThread() {
350 for (IThreadWorkQueue ta : lists.keySet())
351 if (!ta.currentThreadAccess())