| /******************************************************************************* |
| * Copyright (c) 2007, 2012 Wind River Systems and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * 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. |
| */ |
| @Override |
| public List<Runnable> shutdownNow() { |
| return Collections.emptyList(); |
| } |
| } |