/******************************************************************************* * Copyright (c) 2007, 2012 Wind River Systems 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: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.dsf.ui.concurrent; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor; import org.eclipse.cdt.dsf.concurrent.DsfExecutable; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; /** * DSF executor which uses the display thread to run the submitted runnables * and callables. The implementation is based on the default DSF executor * which still creates its own thread. However this thread blocks when running * each executable in the display thread. * * @since 1.0 */ public class DisplayDsfExecutor extends DefaultDsfExecutor { /** * Internal mapping of display objects to executors. */ private static Map<Display, DisplayDsfExecutor> fExecutors = Collections.synchronizedMap( new HashMap<Display, DisplayDsfExecutor>() ); /** * Factory method for display executors. * * <p> * Call this from the GUI thread unless you are certain an instance has * already been created for the given display (creation of new instance will * fail on a non-GUI thread). * * @param display * Display to create an executor for. * @return The new (or re-used) executor. */ public static DisplayDsfExecutor getDisplayDsfExecutor(Display display) { synchronized (fExecutors) { DisplayDsfExecutor executor = fExecutors.get(display); if (executor == null) { executor = new DisplayDsfExecutor(display); fExecutors.put(display, executor); } return executor; } } /** * The display class used by this executor to execute the submitted runnables. */ private final Display fDisplay; private DisplayDsfExecutor(Display display) { super("Display DSF Executor"); //$NON-NLS-1$ fDisplay = display; fDisplay.addListener(SWT.Dispose, new Listener() { @Override public void handleEvent(Event event) { if (event.type == SWT.Dispose) { DisplayDsfExecutor.super.shutdownNow(); } } }); } /** * Override to check if we're in the display thread rather than the helper * thread of the super-class. */ @Override public boolean isInExecutorThread() { return Thread.currentThread().equals(fDisplay.getThread()); } /** * Creates a callable wrapper, which delegates to the display to perform the * operation. The callable blocks the executor thread while each call * is executed in the display thred. * @param <V> Type used in the callable. * @param callable Callable to wrap. * @return Wrapper callable. */ private <V> Callable<V> createSWTDispatchCallable(final Callable<V> callable) { // Check if executable wasn't executed already. if (DEBUG_EXECUTOR && callable instanceof DsfExecutable) { assert !((DsfExecutable)callable).getSubmitted() : "Executable was previously executed."; //$NON-NLS-1$ ((DsfExecutable)callable).setSubmitted(); } return new Callable<V>() { @Override @SuppressWarnings("unchecked") public V call() throws Exception { final Object[] v = new Object[1]; final Throwable[] e = new Throwable[1]; try { fDisplay.syncExec(new Runnable() { @Override public void run() { try { v[0] = callable.call(); } catch(Throwable exception) { e[0] = exception; } } }); } catch (SWTException swtException) { if (swtException.code == SWT.ERROR_DEVICE_DISPOSED) { DisplayDsfExecutor.super.shutdown(); } } if(e[0] instanceof RuntimeException) { throw (RuntimeException) e[0]; } else if (e[0] instanceof Error) { throw (Error) e[0]; } else if(e[0] instanceof Exception) { throw (Exception) e[0]; } return (V) v[0]; } }; } /** * Creates a runnable wrapper, which delegates to the display to perform the * operation. The runnable blocks the executor thread while each call * is executed in the display thred. * @param runnable Runnable to wrap. * @return Wrapper runnable. */ private Runnable createSWTDispatchRunnable(final Runnable runnable) { // Check if executable wasn't executed already. if (DEBUG_EXECUTOR && runnable instanceof DsfExecutable) { assert !((DsfExecutable)runnable).getSubmitted() : "Executable was previously executed."; //$NON-NLS-1$ ((DsfExecutable)runnable).setSubmitted(); } return new Runnable() { @Override public void run() { try { fDisplay.syncExec(new Runnable() { @Override public void run() { runnable.run(); } }); } catch (SWTException swtException) { if (swtException.code == SWT.ERROR_DEVICE_DISPOSED) { DisplayDsfExecutor.super.shutdownNow(); } } } }; } @Override public <V> ScheduledFuture<V> schedule(final Callable<V> callable, long delay, TimeUnit unit) { if (fDisplay.isDisposed()) { if (!super.isShutdown()) super.shutdown(); throw new RejectedExecutionException("Display " + fDisplay + " is disposed."); //$NON-NLS-1$ //$NON-NLS-2$ } return super.schedule(createSWTDispatchCallable(callable), delay, unit); } @Override public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { if (fDisplay.isDisposed()) { if (!super.isShutdown()) super.shutdown(); throw new RejectedExecutionException("Display " + fDisplay + " is disposed."); //$NON-NLS-1$ //$NON-NLS-2$ } return super.schedule(createSWTDispatchRunnable(command), delay, unit); } @Override public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { if (fDisplay.isDisposed()) { if (!super.isShutdown()) super.shutdown(); throw new RejectedExecutionException("Display " + fDisplay + " is disposed."); //$NON-NLS-1$ //$NON-NLS-2$ } return super.scheduleAtFixedRate(createSWTDispatchRunnable(command), initialDelay, period, unit); } @Override public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { if (fDisplay.isDisposed()) { if (!super.isShutdown()) super.shutdown(); throw new RejectedExecutionException("Display " + fDisplay + " is disposed."); //$NON-NLS-1$ //$NON-NLS-2$ } return super.scheduleWithFixedDelay(createSWTDispatchRunnable(command), initialDelay, delay, unit); } @Override public void execute(Runnable command) { if (fDisplay.isDisposed()) { if (!super.isShutdown()) super.shutdown(); throw new RejectedExecutionException("Display " + fDisplay + " is disposed."); //$NON-NLS-1$ //$NON-NLS-2$ } super.execute(createSWTDispatchRunnable(command)); } @Override public <T> Future<T> submit(Callable<T> callable) { if (fDisplay.isDisposed()) { if (!super.isShutdown()) super.shutdown(); throw new RejectedExecutionException("Display " + fDisplay + " is disposed."); //$NON-NLS-1$ //$NON-NLS-2$ } return super.submit(createSWTDispatchCallable(callable)); } @Override public <T> Future<T> submit(Runnable command, T result) { if (fDisplay.isDisposed()) { if (!super.isShutdown()) super.shutdown(); throw new RejectedExecutionException("Display " + fDisplay + " is disposed."); //$NON-NLS-1$ //$NON-NLS-2$ } return super.submit(createSWTDispatchRunnable(command), result); } @Override public Future<?> submit(Runnable command) { if (fDisplay.isDisposed()) { if (!super.isShutdown()) super.shutdown(); throw new RejectedExecutionException("Display " + fDisplay + " is disposed."); //$NON-NLS-1$ //$NON-NLS-2$ } return super.submit(createSWTDispatchRunnable(command)); } /** * Override to prevent clients from shutting down. The executor will be * shut down when the underlying display is discovered to be shut down. */ @Override public void shutdown() { } /** * Override to prevent clients from shutting down. The executor will be * shut down when the underlying display is discovered to be shut down. */ @SuppressWarnings({ "cast", "unchecked" }) @Override public List<Runnable> shutdownNow() { return (List<Runnable>)Collections.EMPTY_LIST; } }