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 }