Mercurial > projects > dwt2
comparison org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/Realm.d @ 95:6208d4f6a277
Added trees for databinding.beans and observable
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Tue, 21 Apr 2009 10:55:51 +0200 |
parents | |
children | b74ac5dfcc06 |
comparison
equal
deleted
inserted
replaced
94:1d37a7813832 | 95:6208d4f6a277 |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2006, 2007 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 * Brad Reynolds - bug 168153 | |
11 *******************************************************************************/ | |
12 | |
13 package org.eclipse.core.databinding.observable; | |
14 | |
15 import org.eclipse.core.databinding.Binding; | |
16 import org.eclipse.core.databinding.util.Policy; | |
17 import org.eclipse.core.internal.databinding.Queue; | |
18 import org.eclipse.core.runtime.ISafeRunnable; | |
19 import org.eclipse.core.runtime.IStatus; | |
20 import org.eclipse.core.runtime.SafeRunner; | |
21 import org.eclipse.core.runtime.Status; | |
22 | |
23 /** | |
24 * A realm defines a context from which objects implementing {@link IObservable} | |
25 * must be accessed, and on which these objects will notify their listeners. To | |
26 * bridge between observables from different realms, subclasses of | |
27 * {@link Binding} can be used. | |
28 * <p> | |
29 * A block of code is said to be executing within a realm if calling | |
30 * {@link #isCurrent()} from that block returns true. Code reached by calling | |
31 * methods from that block will execute within the same realm, with the | |
32 * exception of methods on this class that can be used to execute code within a | |
33 * specific realm. Clients can use {@link #syncExeccast(Runnable)}, | |
34 * {@link #asyncExeccast(Runnable)}, or {@link #execcast(Runnable)} to execute a | |
35 * runnable within this realm. Note that using {@link #syncExeccast(Runnable)} can | |
36 * lead to deadlocks and should be avoided if the current thread holds any | |
37 * locks. | |
38 * </p> | |
39 * <p> | |
40 * It is instructive to think about possible implementations of Realm: It can be | |
41 * based on executing on a designated thread such as a UI thread, or based on | |
42 * holding a lock. In the former case, calling syncExec on a realm that is not | |
43 * the current realm will execute the given runnable on a different thread (the | |
44 * designated thread). In the latter case, calling syncExec may execute the | |
45 * given runnable on the calling thread, but calling | |
46 * {@link #asyncExeccast(Runnable)} will execute the given runnable on a different | |
47 * thread. Therefore, no assumptions can be made about the thread that will | |
48 * execute arguments to {@link #asyncExeccast(Runnable)}, | |
49 * {@link #syncExeccast(Runnable)}, or {@link #execcast(Runnable)}. | |
50 * </p> | |
51 * <p> | |
52 * It is possible that a block of code is executing within more than one realm. | |
53 * This can happen for implementations of Realm that are based on holding a lock | |
54 * but don't use a separate thread to run runnables given to | |
55 * {@link #syncExeccast(Runnable)}. Realm implementations of this kind should be | |
56 * appropriately documented because it increases the opportunity for deadlock. | |
57 * </p> | |
58 * <p> | |
59 * Some implementations of {@link IObservable} provide constructors which do not | |
60 * take a Realm argument and are specified to create the observable instance | |
61 * with the current default realm. The default realm can be set for the | |
62 * currently executing thread by using {@link #runWithDefault(Realm, Runnable)}. | |
63 * Note that the default realm does not have to be the current realm. | |
64 * </p> | |
65 * <p> | |
66 * Subclasses must override at least one of asyncExec()/syncExec(). For realms | |
67 * based on a designated thread, it may be easier to implement asyncExec and | |
68 * keep the default implementation of syncExec. For realms based on holding a | |
69 * lock, it may be easier to implement syncExec and keep the default | |
70 * implementation of asyncExec. | |
71 * </p> | |
72 * | |
73 * @since 1.0 | |
74 * | |
75 * @see IObservable | |
76 */ | |
77 public abstract class Realm { | |
78 | |
79 private static ThreadLocal defaultRealm = new ThreadLocal(); | |
80 | |
81 /** | |
82 * Returns the default realm for the calling thread, or <code>null</code> | |
83 * if no default realm has been set. | |
84 * | |
85 * @return the default realm, or <code>null</code> | |
86 */ | |
87 public static Realm getDefault() { | |
88 return cast(Realm) defaultRealm.get(); | |
89 } | |
90 | |
91 /** | |
92 * Sets the default realm for the calling thread, returning the current | |
93 * default thread. This method is inherently unsafe, it is recommended to | |
94 * use {@link #runWithDefault(Realm, Runnable)} instead. This method is | |
95 * exposed to subclasses to facilitate testing. | |
96 * | |
97 * @param realm | |
98 * the new default realm, or <code>null</code> | |
99 * @return the previous default realm, or <code>null</code> | |
100 */ | |
101 protected static Realm setDefault(Realm realm) { | |
102 Realm oldValue = getDefault(); | |
103 defaultRealm.set(realm); | |
104 return oldValue; | |
105 } | |
106 | |
107 /** | |
108 * @return true if the caller is executing in this realm. This method must | |
109 * not have side-effects (such as, for example, implicitly placing | |
110 * the caller in this realm). | |
111 */ | |
112 abstract public bool isCurrent(); | |
113 | |
114 private Thread workerThread; | |
115 | |
116 Queue workQueue = new Queue(); | |
117 | |
118 /** | |
119 * Runs the given runnable. If an exception occurs within the runnable, it | |
120 * is logged and not re-thrown. If the runnable implements | |
121 * {@link ISafeRunnable}, the exception is passed to its | |
122 * <code>handleException<code> method. | |
123 * | |
124 * @param runnable | |
125 */ | |
126 protected static void safeRun(final Runnable runnable) { | |
127 ISafeRunnable safeRunnable; | |
128 if ( null !is cast(ISafeRunnable)runnable ) { | |
129 safeRunnable = cast(ISafeRunnable) runnable; | |
130 } else { | |
131 safeRunnable = new class() ISafeRunnable { | |
132 public void handleException(Throwable exception) { | |
133 Policy | |
134 .getLog() | |
135 .log( | |
136 new Status( | |
137 IStatus.ERROR, | |
138 Policy.JFACE_DATABINDING, | |
139 IStatus.OK, | |
140 "Unhandled exception: " + exception.getMessage(), exception)); //$NON-NLS-1$ | |
141 } | |
142 public void run() { | |
143 runnable.run(); | |
144 } | |
145 }; | |
146 } | |
147 SafeRunner.run(safeRunnable); | |
148 } | |
149 | |
150 /** | |
151 * Causes the <code>run()</code> method of the runnable to be invoked from | |
152 * within this realm. If the caller is executing in this realm, the | |
153 * runnable's run method is invoked directly, otherwise it is run at the | |
154 * next reasonable opportunity using asyncExec. | |
155 * <p> | |
156 * If the given runnable is an instance of {@link ISafeRunnable}, its | |
157 * exception handler method will be called if any exceptions occur while | |
158 * running it. Otherwise, the exception will be logged. | |
159 * </p> | |
160 * | |
161 * @param runnable | |
162 */ | |
163 public void exec(Runnable runnable) { | |
164 if (isCurrent()) { | |
165 safeRun(runnable); | |
166 } else { | |
167 asyncExec(runnable); | |
168 } | |
169 } | |
170 | |
171 /** | |
172 * Causes the <code>run()</code> method of the runnable to be invoked from | |
173 * within this realm at the next reasonable opportunity. The caller of this | |
174 * method continues to run in parallel, and is not notified when the | |
175 * runnable has completed. | |
176 * <p> | |
177 * If the given runnable is an instance of {@link ISafeRunnable}, its | |
178 * exception handler method will be called if any exceptions occur while | |
179 * running it. Otherwise, the exception will be logged. | |
180 * </p> | |
181 * <p> | |
182 * Subclasses should use {@link #safeRuncast(Runnable)} to run the runnable. | |
183 * </p> | |
184 * | |
185 * @param runnable | |
186 */ | |
187 public void asyncExec(Runnable runnable) { | |
188 synchronized (workQueue) { | |
189 ensureWorkerThreadIsRunning(); | |
190 workQueue.enqueue(runnable); | |
191 workQueue.notifyAll(); | |
192 } | |
193 } | |
194 | |
195 /** | |
196 * | |
197 */ | |
198 private void ensureWorkerThreadIsRunning() { | |
199 if (workerThread is null) { | |
200 workerThread = new class() Thread { | |
201 public void run() { | |
202 try { | |
203 while (true) { | |
204 Runnable work = null; | |
205 synchronized (workQueue) { | |
206 while (workQueue.isEmpty()) { | |
207 workQueue.wait(); | |
208 } | |
209 work = cast(Runnable) workQueue.dequeue(); | |
210 } | |
211 syncExec(work); | |
212 } | |
213 } catch (InterruptedException e) { | |
214 // exit | |
215 } | |
216 } | |
217 }; | |
218 workerThread.start(); | |
219 } | |
220 } | |
221 | |
222 /** | |
223 * Causes the <code>run()</code> method of the runnable to be invoked from | |
224 * within this realm at the next reasonable opportunity. This method is | |
225 * blocking the caller until the runnable completes. | |
226 * <p> | |
227 * If the given runnable is an instance of {@link ISafeRunnable}, its | |
228 * exception handler method will be called if any exceptions occur while | |
229 * running it. Otherwise, the exception will be logged. | |
230 * </p> | |
231 * <p> | |
232 * Subclasses should use {@link #safeRuncast(Runnable)} to run the runnable. | |
233 * </p> | |
234 * <p> | |
235 * Note: This class is not meant to be called by clients and therefore has | |
236 * only protected access. | |
237 * </p> | |
238 * | |
239 * @param runnable | |
240 */ | |
241 protected void syncExec(Runnable runnable) { | |
242 SyncRunnable syncRunnable = new SyncRunnable(runnable); | |
243 asyncExec(syncRunnable); | |
244 synchronized (syncRunnable) { | |
245 while (!syncRunnable.hasRun) { | |
246 try { | |
247 syncRunnable.wait(); | |
248 } catch (InterruptedException e) { | |
249 Thread.currentThread().interrupt(); | |
250 } | |
251 } | |
252 } | |
253 } | |
254 | |
255 static class SyncRunnable : Runnable { | |
256 bool hasRun = false; | |
257 | |
258 private Runnable runnable; | |
259 | |
260 this(Runnable runnable) { | |
261 this.runnable = runnable; | |
262 } | |
263 | |
264 public void run() { | |
265 try { | |
266 safeRun(runnable); | |
267 } finally { | |
268 synchronized (this) { | |
269 hasRun = true; | |
270 this.notifyAll(); | |
271 } | |
272 } | |
273 } | |
274 } | |
275 | |
276 /** | |
277 * Sets the provided <code>realm</code> as the default for the duration of | |
278 * {@link Runnable#run()} and resets the previous realm after completion. | |
279 * Note that this will not set the given realm as the current realm. | |
280 * | |
281 * @param realm | |
282 * @param runnable | |
283 */ | |
284 public static void runWithDefault(Realm realm, Runnable runnable) { | |
285 Realm oldRealm = Realm.getDefault(); | |
286 try { | |
287 defaultRealm.set(realm); | |
288 runnable.run(); | |
289 } finally { | |
290 defaultRealm.set(oldRealm); | |
291 } | |
292 } | |
293 } |