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