/* * Copyright (c) 2012, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.tools.ui.test.runnable; import org.eclipse.swt.widgets.Display; import java.util.ArrayList; /** * Performs operations in the UI thread. If the {@link #run()} method of this class is called from * an non-UI thread, the instance ensures that it runs in the UI thread by invoking * {@link Display#syncExec(Runnable)}, else it executes in the UI thread. All operations are * blocking operations. */ public abstract class UIThreadRunnable implements Runnable { /** * The shared display instance. */ private static Display DISPLAY = null; /** * Gets all the threads in the VM. * * @return all the threads in the VM. */ public static Thread[] allThreads() { ThreadGroup threadGroup = primaryThreadGroup(); Thread[] threads = new Thread[64]; int enumerate = threadGroup.enumerate(threads, true); Thread[] result = new Thread[enumerate]; System.arraycopy(threads, 0, result, 0, enumerate); return result; } /** * Executes the {@code toExecute} on the UI thread asynchronously, and does not block the calling * thread. * * @param display the display on which toExecute must be executed. * @param toExecute the runnable to execute. */ public static void asyncExec(Display display, final VoidResult toExecute) { new UIThreadRunnable(display, true) { @Override protected void doRun() { toExecute.run(); } }.run(); } /** * Executes the {@code toExecute} on the UI thread asynchronously, and does not block the calling * thread. * * @param toExecute the runnable to execute. */ public static void asyncExec(final VoidResult toExecute) { asyncExec(display(), toExecute); } /** * Caches the display for later use. * * @return the display. */ public static Display display() { if ((DISPLAY == null) || DISPLAY.isDisposed()) { DISPLAY = null; Thread[] allThreads = allThreads(); for (Thread thread : allThreads) { Display d = Display.findDisplay(thread); if (d != null) { DISPLAY = d; break; } } if (DISPLAY == null) { throw new IllegalStateException("Could not find a display"); //$NON-NLS-1$ } } return DISPLAY; } /** * Return true if the current thread is the UI thread. * * @param display the display * @return <code>true</code> if the current thread is the UI thread, <code>false</code> otherwise. */ public static boolean isUIThread(Display display) { return display.getThread() == Thread.currentThread(); } /** * Gets the primary thread group. * * @return the top level thread group. */ public static ThreadGroup primaryThreadGroup() { ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); while (threadGroup.getParent() != null) { threadGroup = threadGroup.getParent(); } return threadGroup; } /** * Executes the {@code toExecute} on the display thread, and blocks the calling thread. * * @param <T> the type of the result. * @param toExecute the runnable to execute. * @return the object result of execution on the UI thread. */ public static <T> T[] syncExec(final ArrayResult<T> toExecute) { return syncExec(display(), toExecute); } /** * Executes the {@code toExecute} on the display thread, and blocks the calling thread. * * @param <T> the type of the result. * @param display the display on which toExecute must be executed. * @param toExecute the runnable to execute. * @return the object result of execution on the UI thread. */ public static <T> T[] syncExec(Display display, final ArrayResult<T> toExecute) { final ArrayList<T[]> arrayList = new ArrayList<T[]>(); new UIThreadRunnable(display) { @Override protected void doRun() { T[] run = toExecute.run(); arrayList.add(run); } }.run(); return arrayList.get(0); } /** * Executes the {@code toExecute} on the display thread, and blocks the calling thread. * * @param <T> the type of the result. * @param display the display on which toExecute must be executed. * @param toExecute the runnable to execute. * @return the object result of execution on the UI thread. */ public static <T> T syncExec(Display display, final Result<T> toExecute) { final ArrayList<T> arrayList = new ArrayList<T>(); new UIThreadRunnable(display) { @Override protected void doRun() { arrayList.add(toExecute.run()); } }.run(); return arrayList.get(0); } /** * Executes the {@code toExecute} on the display thread, and blocks the calling thread. * * @param display the display on which toExecute must be executed. * @param toExecute the runnable to execute. */ public static void syncExec(Display display, final VoidResult toExecute) { new UIThreadRunnable(display) { @Override protected void doRun() { toExecute.run(); } }.run(); } /** * Executes the {@code toExecute} on the UI thread, and blocks the calling thread. * * @param <T> the type of the result. * @param toExecute the runnable to execute. * @return the result of executing result on the UI thread. */ public static <T> T syncExec(final Result<T> toExecute) { return syncExec(display(), toExecute); } /** * Executes the {@code toExecute} on the UI thread, and blocks the calling thread. * * @param toExecute the runnable to execute. */ public static void syncExec(final VoidResult toExecute) { syncExec(display(), toExecute); } /** the display on which runnables must be executed. */ protected final Display display; /** * A flag to denote if the runnable should execute asynchronously. */ private final boolean async; /** * Runs synchronously in the UI thread. * * @param display The display to be used. */ private UIThreadRunnable(Display display) { this(display, false); } /** * A private contructor use to create this object. * * @param display The display to use. * @param async if the thread should run asynchronously or not. * @see Display#syncExec(Runnable) * @see Display#asyncExec(Runnable) */ private UIThreadRunnable(Display display, boolean async) { this.display = display; this.async = async; } /** * This method is intelligent to execute in the UI thread. */ @Override public void run() { if ((display == null) || display.isDisposed()) { return; } if (!isUIThread(display)) { if (async) { display.asyncExec(runnable()); } else { display.syncExec(runnable()); } } else { doRun(); } } /** * Performs the run in the UI Thread. * <p> * This MUST be invoked in the UI thread. * </p> */ protected abstract void doRun(); /** * This dispatched events in the UI thread. * <p> * This must be called in the UI thread only. This method does not execute in a syncexec/asyncexec * block * </p> */ private void dispatchAllEvents() { display.wake(); // while (true) // if (!display.readAndDispatch()) // break; } /** * A runnable instance that is used internally. * * @return The runnable instance. */ private Runnable runnable() { final Runnable runnable = new Runnable() { @Override public void run() { doRun(); dispatchAllEvents(); } }; return runnable; } }