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