| /******************************************************************************* |
| * Copyright (c) 2006, 2007 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Brad Reynolds - bug 168153 |
| *******************************************************************************/ |
| |
| package org.eclipse.core.databinding.observable; |
| |
| import org.eclipse.core.databinding.Binding; |
| import org.eclipse.core.databinding.util.Policy; |
| import org.eclipse.core.internal.databinding.Queue; |
| import org.eclipse.core.runtime.ISafeRunnable; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.SafeRunner; |
| import org.eclipse.core.runtime.Status; |
| |
| /** |
| * A realm defines a context from which objects implementing {@link IObservable} |
| * must be accessed, and on which these objects will notify their listeners. To |
| * bridge between observables from different realms, subclasses of |
| * {@link Binding} can be used. |
| * <p> |
| * A block of code is said to be executing within a realm if calling |
| * {@link #isCurrent()} from that block returns true. Code reached by calling |
| * methods from that block will execute within the same realm, with the |
| * exception of methods on this class that can be used to execute code within a |
| * specific realm. Clients can use {@link #syncExec(Runnable)}, |
| * {@link #asyncExec(Runnable)}, or {@link #exec(Runnable)} to execute a |
| * runnable within this realm. Note that using {@link #syncExec(Runnable)} can |
| * lead to deadlocks and should be avoided if the current thread holds any |
| * locks. |
| * </p> |
| * <p> |
| * It is instructive to think about possible implementations of Realm: It can be |
| * based on executing on a designated thread such as a UI thread, or based on |
| * holding a lock. In the former case, calling syncExec on a realm that is not |
| * the current realm will execute the given runnable on a different thread (the |
| * designated thread). In the latter case, calling syncExec may execute the |
| * given runnable on the calling thread, but calling |
| * {@link #asyncExec(Runnable)} will execute the given runnable on a different |
| * thread. Therefore, no assumptions can be made about the thread that will |
| * execute arguments to {@link #asyncExec(Runnable)}, |
| * {@link #syncExec(Runnable)}, or {@link #exec(Runnable)}. |
| * </p> |
| * <p> |
| * It is possible that a block of code is executing within more than one realm. |
| * This can happen for implementations of Realm that are based on holding a lock |
| * but don't use a separate thread to run runnables given to |
| * {@link #syncExec(Runnable)}. Realm implementations of this kind should be |
| * appropriately documented because it increases the opportunity for deadlock. |
| * </p> |
| * <p> |
| * Some implementations of {@link IObservable} provide constructors which do not |
| * take a Realm argument and are specified to create the observable instance |
| * with the current default realm. The default realm can be set for the |
| * currently executing thread by using {@link #runWithDefault(Realm, Runnable)}. |
| * Note that the default realm does not have to be the current realm. |
| * </p> |
| * <p> |
| * Subclasses must override at least one of asyncExec()/syncExec(). For realms |
| * based on a designated thread, it may be easier to implement asyncExec and |
| * keep the default implementation of syncExec. For realms based on holding a |
| * lock, it may be easier to implement syncExec and keep the default |
| * implementation of asyncExec. |
| * </p> |
| * |
| * @since 1.0 |
| * |
| * @see IObservable |
| */ |
| public abstract class Realm { |
| |
| private static ThreadLocal defaultRealm = new ThreadLocal(); |
| |
| /** |
| * Returns the default realm for the calling thread, or <code>null</code> |
| * if no default realm has been set. |
| * |
| * @return the default realm, or <code>null</code> |
| */ |
| public static Realm getDefault() { |
| return (Realm) defaultRealm.get(); |
| } |
| |
| /** |
| * Sets the default realm for the calling thread, returning the current |
| * default thread. This method is inherently unsafe, it is recommended to |
| * use {@link #runWithDefault(Realm, Runnable)} instead. This method is |
| * exposed to subclasses to facilitate testing. |
| * |
| * @param realm |
| * the new default realm, or <code>null</code> |
| * @return the previous default realm, or <code>null</code> |
| */ |
| protected static Realm setDefault(Realm realm) { |
| Realm oldValue = getDefault(); |
| defaultRealm.set(realm); |
| return oldValue; |
| } |
| |
| /** |
| * @return true if the caller is executing in this realm. This method must |
| * not have side-effects (such as, for example, implicitly placing |
| * the caller in this realm). |
| */ |
| abstract public boolean isCurrent(); |
| |
| private Thread workerThread; |
| |
| Queue workQueue = new Queue(); |
| |
| /** |
| * Runs the given runnable. If an exception occurs within the runnable, it |
| * is logged and not re-thrown. If the runnable implements |
| * {@link ISafeRunnable}, the exception is passed to its |
| * <code>handleException<code> method. |
| * |
| * @param runnable |
| */ |
| protected static void safeRun(final Runnable runnable) { |
| ISafeRunnable safeRunnable; |
| if (runnable instanceof ISafeRunnable) { |
| safeRunnable = (ISafeRunnable) runnable; |
| } else { |
| safeRunnable = new ISafeRunnable() { |
| public void handleException(Throwable exception) { |
| Policy |
| .getLog() |
| .log( |
| new Status( |
| IStatus.ERROR, |
| Policy.JFACE_DATABINDING, |
| IStatus.OK, |
| "Unhandled exception: " + exception.getMessage(), exception)); //$NON-NLS-1$ |
| } |
| public void run() throws Exception { |
| runnable.run(); |
| } |
| }; |
| } |
| SafeRunner.run(safeRunnable); |
| } |
| |
| /** |
| * Causes the <code>run()</code> method of the runnable to be invoked from |
| * within this realm. If the caller is executing in this realm, the |
| * runnable's run method is invoked directly, otherwise it is run at the |
| * next reasonable opportunity using asyncExec. |
| * <p> |
| * If the given runnable is an instance of {@link ISafeRunnable}, its |
| * exception handler method will be called if any exceptions occur while |
| * running it. Otherwise, the exception will be logged. |
| * </p> |
| * |
| * @param runnable |
| */ |
| public void exec(Runnable runnable) { |
| if (isCurrent()) { |
| safeRun(runnable); |
| } else { |
| asyncExec(runnable); |
| } |
| } |
| |
| /** |
| * Causes the <code>run()</code> method of the runnable to be invoked from |
| * within this realm at the next reasonable opportunity. The caller of this |
| * method continues to run in parallel, and is not notified when the |
| * runnable has completed. |
| * <p> |
| * If the given runnable is an instance of {@link ISafeRunnable}, its |
| * exception handler method will be called if any exceptions occur while |
| * running it. Otherwise, the exception will be logged. |
| * </p> |
| * <p> |
| * Subclasses should use {@link #safeRun(Runnable)} to run the runnable. |
| * </p> |
| * |
| * @param runnable |
| */ |
| public void asyncExec(Runnable runnable) { |
| synchronized (workQueue) { |
| ensureWorkerThreadIsRunning(); |
| workQueue.enqueue(runnable); |
| workQueue.notifyAll(); |
| } |
| } |
| |
| /** |
| * |
| */ |
| private void ensureWorkerThreadIsRunning() { |
| if (workerThread == null) { |
| workerThread = new Thread() { |
| public void run() { |
| try { |
| while (true) { |
| Runnable work = null; |
| synchronized (workQueue) { |
| while (workQueue.isEmpty()) { |
| workQueue.wait(); |
| } |
| work = (Runnable) workQueue.dequeue(); |
| } |
| syncExec(work); |
| } |
| } catch (InterruptedException e) { |
| // exit |
| } |
| } |
| }; |
| workerThread.start(); |
| } |
| } |
| |
| /** |
| * Causes the <code>run()</code> method of the runnable to be invoked from |
| * within this realm at the next reasonable opportunity. This method is |
| * blocking the caller until the runnable completes. |
| * <p> |
| * If the given runnable is an instance of {@link ISafeRunnable}, its |
| * exception handler method will be called if any exceptions occur while |
| * running it. Otherwise, the exception will be logged. |
| * </p> |
| * <p> |
| * Subclasses should use {@link #safeRun(Runnable)} to run the runnable. |
| * </p> |
| * <p> |
| * Note: This class is not meant to be called by clients and therefore has |
| * only protected access. |
| * </p> |
| * |
| * @param runnable |
| */ |
| protected void syncExec(Runnable runnable) { |
| SyncRunnable syncRunnable = new SyncRunnable(runnable); |
| asyncExec(syncRunnable); |
| synchronized (syncRunnable) { |
| while (!syncRunnable.hasRun) { |
| try { |
| syncRunnable.wait(); |
| } catch (InterruptedException e) { |
| Thread.currentThread().interrupt(); |
| } |
| } |
| } |
| } |
| |
| static class SyncRunnable implements Runnable { |
| boolean hasRun = false; |
| |
| private Runnable runnable; |
| |
| SyncRunnable(Runnable runnable) { |
| this.runnable = runnable; |
| } |
| |
| public void run() { |
| try { |
| safeRun(runnable); |
| } finally { |
| synchronized (this) { |
| hasRun = true; |
| this.notifyAll(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Sets the provided <code>realm</code> as the default for the duration of |
| * {@link Runnable#run()} and resets the previous realm after completion. |
| * Note that this will not set the given realm as the current realm. |
| * |
| * @param realm |
| * @param runnable |
| */ |
| public static void runWithDefault(Realm realm, Runnable runnable) { |
| Realm oldRealm = Realm.getDefault(); |
| try { |
| defaultRealm.set(realm); |
| runnable.run(); |
| } finally { |
| defaultRealm.set(oldRealm); |
| } |
| } |
| } |