Mercurial > projects > dwt2
annotate org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/Realm.d @ 125:c43718956f21 default tip
Updated the snippets status.
author | Jacob Carlborg <doob@me.com> |
---|---|
date | Thu, 11 Aug 2011 19:55:14 +0200 |
parents | 5d5bd660917f |
children |
rev | line source |
---|---|
78 | 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 | |
99
5d5bd660917f
build some databind snippets
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
81 private static ThreadLocal defaultRealm_; |
5d5bd660917f
build some databind snippets
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
82 private static ThreadLocal defaultRealm(){ |
5d5bd660917f
build some databind snippets
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
83 if( defaultRealm_ is null ){ |
5d5bd660917f
build some databind snippets
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
84 synchronized{ |
5d5bd660917f
build some databind snippets
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
85 if( defaultRealm_ is null ){ |
5d5bd660917f
build some databind snippets
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
86 defaultRealm_ = new ThreadLocal(); |
5d5bd660917f
build some databind snippets
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
87 } |
5d5bd660917f
build some databind snippets
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
88 } |
5d5bd660917f
build some databind snippets
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
89 } |
5d5bd660917f
build some databind snippets
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
90 return defaultRealm_; |
85 | 91 } |
99
5d5bd660917f
build some databind snippets
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
92 |
85 | 93 this(){ |
94 workQueue = new Queue(); | |
95 } | |
78 | 96 |
97 /** | |
98 * Returns the default realm for the calling thread, or <code>null</code> | |
99 * if no default realm has been set. | |
100 * | |
101 * @return the default realm, or <code>null</code> | |
102 */ | |
103 public static Realm getDefault() { | |
104 return cast(Realm) defaultRealm.get(); | |
105 } | |
106 | |
107 /** | |
108 * Sets the default realm for the calling thread, returning the current | |
109 * default thread. This method is inherently unsafe, it is recommended to | |
110 * use {@link #runWithDefault(Realm, Runnable)} instead. This method is | |
111 * exposed to subclasses to facilitate testing. | |
112 * | |
113 * @param realm | |
114 * the new default realm, or <code>null</code> | |
115 * @return the previous default realm, or <code>null</code> | |
116 */ | |
117 protected static Realm setDefault(Realm realm) { | |
118 Realm oldValue = getDefault(); | |
119 defaultRealm.set(realm); | |
120 return oldValue; | |
121 } | |
122 | |
123 /** | |
124 * @return true if the caller is executing in this realm. This method must | |
125 * not have side-effects (such as, for example, implicitly placing | |
126 * the caller in this realm). | |
127 */ | |
128 abstract public bool isCurrent(); | |
129 | |
130 private Thread workerThread; | |
131 | |
85 | 132 Queue workQueue; |
78 | 133 |
134 /** | |
135 * Runs the given runnable. If an exception occurs within the runnable, it | |
136 * is logged and not re-thrown. If the runnable implements | |
137 * {@link ISafeRunnable}, the exception is passed to its | |
138 * <code>handleException<code> method. | |
139 * | |
140 * @param runnable | |
141 */ | |
142 protected static void safeRun(Runnable runnable) { | |
143 ISafeRunnable safeRunnable; | |
144 if ( null !is cast(ISafeRunnable)runnable ) { | |
145 safeRunnable = cast(ISafeRunnable) runnable; | |
146 } else { | |
147 safeRunnable = new class(runnable) ISafeRunnable { | |
148 Runnable runnable_; | |
149 this(Runnable r){runnable_=r;} | |
150 public void handleException(Throwable exception) { | |
151 Policy | |
152 .getLog() | |
153 .log( | |
154 new Status( | |
155 IStatus.ERROR, | |
156 Policy.JFACE_DATABINDING, | |
157 IStatus.OK, | |
85 | 158 "Unhandled exception: " ~ exception.msg, exception)); //$NON-NLS-1$ |
78 | 159 } |
160 public void run() { | |
161 runnable_.run(); | |
162 } | |
163 }; | |
164 } | |
165 SafeRunner.run(safeRunnable); | |
166 } | |
167 | |
168 /** | |
169 * Causes the <code>run()</code> method of the runnable to be invoked from | |
170 * within this realm. If the caller is executing in this realm, the | |
171 * runnable's run method is invoked directly, otherwise it is run at the | |
172 * next reasonable opportunity using asyncExec. | |
173 * <p> | |
174 * If the given runnable is an instance of {@link ISafeRunnable}, its | |
175 * exception handler method will be called if any exceptions occur while | |
176 * running it. Otherwise, the exception will be logged. | |
177 * </p> | |
178 * | |
179 * @param runnable | |
180 */ | |
181 public void exec(Runnable runnable) { | |
182 if (isCurrent()) { | |
183 safeRun(runnable); | |
184 } else { | |
185 asyncExec(runnable); | |
186 } | |
187 } | |
188 | |
189 /** | |
190 * Causes the <code>run()</code> method of the runnable to be invoked from | |
191 * within this realm at the next reasonable opportunity. The caller of this | |
192 * method continues to run in parallel, and is not notified when the | |
193 * runnable has completed. | |
194 * <p> | |
195 * If the given runnable is an instance of {@link ISafeRunnable}, its | |
196 * exception handler method will be called if any exceptions occur while | |
197 * running it. Otherwise, the exception will be logged. | |
198 * </p> | |
199 * <p> | |
200 * Subclasses should use {@link #safeRuncast(Runnable)} to run the runnable. | |
201 * </p> | |
202 * | |
203 * @param runnable | |
204 */ | |
205 public void asyncExec(Runnable runnable) { | |
206 synchronized (workQueue) { | |
207 ensureWorkerThreadIsRunning(); | |
85 | 208 workQueue.enqueue(cast(Object)runnable); |
78 | 209 workQueue.notifyAll(); |
210 } | |
211 } | |
212 | |
213 /** | |
214 * | |
215 */ | |
216 private void ensureWorkerThreadIsRunning() { | |
217 if (workerThread is null) { | |
218 workerThread = new class() Thread { | |
219 public void run() { | |
220 try { | |
221 while (true) { | |
222 Runnable work = null; | |
223 synchronized (workQueue) { | |
224 while (workQueue.isEmpty()) { | |
225 workQueue.wait(); | |
226 } | |
227 work = cast(Runnable) workQueue.dequeue(); | |
228 } | |
229 syncExec(work); | |
230 } | |
231 } catch (InterruptedException e) { | |
232 // exit | |
233 } | |
234 } | |
235 }; | |
236 workerThread.start(); | |
237 } | |
238 } | |
239 | |
240 /** | |
241 * Causes the <code>run()</code> method of the runnable to be invoked from | |
242 * within this realm at the next reasonable opportunity. This method is | |
243 * blocking the caller until the runnable completes. | |
244 * <p> | |
245 * If the given runnable is an instance of {@link ISafeRunnable}, its | |
246 * exception handler method will be called if any exceptions occur while | |
247 * running it. Otherwise, the exception will be logged. | |
248 * </p> | |
249 * <p> | |
250 * Subclasses should use {@link #safeRuncast(Runnable)} to run the runnable. | |
251 * </p> | |
252 * <p> | |
253 * Note: This class is not meant to be called by clients and therefore has | |
254 * only protected access. | |
255 * </p> | |
256 * | |
257 * @param runnable | |
258 */ | |
259 protected void syncExec(Runnable runnable) { | |
260 SyncRunnable syncRunnable = new SyncRunnable(runnable); | |
261 asyncExec(syncRunnable); | |
262 synchronized (syncRunnable) { | |
263 while (!syncRunnable.hasRun) { | |
264 try { | |
265 syncRunnable.wait(); | |
266 } catch (InterruptedException e) { | |
267 Thread.currentThread().interrupt(); | |
268 } | |
269 } | |
270 } | |
271 } | |
272 | |
273 static class SyncRunnable : Runnable { | |
274 bool hasRun = false; | |
275 | |
276 private Runnable runnable; | |
277 | |
278 this(Runnable runnable) { | |
279 this.runnable = runnable; | |
280 } | |
281 | |
282 public void run() { | |
283 try { | |
284 safeRun(runnable); | |
285 } finally { | |
286 synchronized (this) { | |
287 hasRun = true; | |
288 this.notifyAll(); | |
289 } | |
290 } | |
291 } | |
85 | 292 void notifyAll(){ |
293 implMissing( __FILE__, __LINE__ ); | |
294 } | |
295 void wait(){ | |
296 implMissing( __FILE__, __LINE__ ); | |
297 } | |
78 | 298 } |
299 | |
300 /** | |
301 * Sets the provided <code>realm</code> as the default for the duration of | |
302 * {@link Runnable#run()} and resets the previous realm after completion. | |
303 * Note that this will not set the given realm as the current realm. | |
304 * | |
305 * @param realm | |
306 * @param runnable | |
307 */ | |
308 public static void runWithDefault(Realm realm, Runnable runnable) { | |
309 Realm oldRealm = Realm.getDefault(); | |
310 try { | |
311 defaultRealm.set(realm); | |
312 runnable.run(); | |
313 } finally { | |
314 defaultRealm.set(oldRealm); | |
315 } | |
316 } | |
317 } |