diff options
Diffstat (limited to 'target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent')
13 files changed, 1296 insertions, 0 deletions
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/Executors.java b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/Executors.java new file mode 100644 index 000000000..2344035b1 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/Executors.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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.tcf.te.runtime.concurrent; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.tcf.te.runtime.concurrent.interfaces.IExecutor; +import org.eclipse.tcf.te.runtime.extensions.AbstractExtensionPointManager; +import org.eclipse.tcf.te.runtime.extensions.ExecutableExtensionProxy; + + +/** + * Class is providing the entry points to create or query the executor service + * instances. + */ +public final class Executors { + + /** + * Execution service extension point manager. + */ + protected static class ExecutorServiceExtensionPointManager extends AbstractExtensionPointManager<IExecutor> { + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.runtime.extensions.AbstractExtensionPointManager#getExtensionPointId() + */ + @Override + protected String getExtensionPointId() { + return "org.eclipse.tcf.te.runtime.concurrent.executorServices"; //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.runtime.extensions.AbstractExtensionPointManager#getConfigurationElementName() + */ + @Override + protected String getConfigurationElementName() { + return "executorService"; //$NON-NLS-1$ + } + + /** + * Returns the list of all contributed executors. + * + * @return The list of contributed executors, or an empty array. + */ + public IExecutor[] getExecutors() { + List<IExecutor> contributions = new ArrayList<IExecutor>(); + Collection<ExecutableExtensionProxy<IExecutor>> proxies = getExtensions().values(); + for (ExecutableExtensionProxy<IExecutor> proxy : proxies) + if (proxy.getInstance() != null + && !contributions.contains(proxy.getInstance())) + contributions.add(proxy.getInstance()); + + return contributions.toArray(new IExecutor[contributions.size()]); + } + + /** + * Returns the executor identified by its unique id. If no executor with + * the specified id is registered, <code>null</code> is returned. + * + * @param id + * The unique id of the executor. Must not be + * <code>null</code> + * @param newInstance + * Specify <code>true</code> to get a new executor service + * instance, <code>false</code> otherwise. + * + * @return The executor instance or <code>null</code>. + */ + public IExecutor getExecutor(String id, boolean newInstance) { + Assert.isNotNull(id); + + IExecutor executorService = null; + if (getExtensions().containsKey(id)) { + ExecutableExtensionProxy<IExecutor> proxy = getExtensions().get(id); + // Get the extension instance + executorService = newInstance ? proxy.newInstance() : proxy.getInstance(); + } + + return executorService; + } + } + + // Reference to the executor service extension point manager + private final static ExecutorServiceExtensionPointManager EXTENSION_POINT_MANAGER = new ExecutorServiceExtensionPointManager(); + + /** + * Constructor. + * <p> + * <b>Note:</b> The class cannot be instantiated. + */ + private Executors() { + } + + /** + * Creates an instance of the executor registered with the specified id. If + * no executor is registered under the given id, the method will return + * <code>null</code>. + * + * @param id + * The id of the executor. Must not be <code>null</code>. + * @return The new executor instance or <code>null</code>. + */ + public static IExecutor newExecutor(String id) { + Assert.isNotNull(id); + return EXTENSION_POINT_MANAGER.getExecutor(id, true); + } + + /** + * Returns the shared instance of the executor registered with the specified + * id. If the shared instance hasn't been created yet, the instance will be + * created and saved. Subsequent calls to this method with the same id will + * return always the same executor instance. If no executor is registered + * under the given id, the method will return <code>null</code>. + * + * @param id + * The id of the executor. Must not be <code>null</code>. + * @return The new executor instance or <code>null</code>. + */ + public static IExecutor getSharedExecutor(String id) { + Assert.isNotNull(id); + return EXTENSION_POINT_MANAGER.getExecutor(id, false); + } + + /** + * Returns the shared instances of all registered executors. + * + * @return All executor instances or an empty array. + */ + public static IExecutor[] getAllSharedExecutors() { + return EXTENSION_POINT_MANAGER.getExecutors(); + } +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/activator/CoreBundleActivator.java b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/activator/CoreBundleActivator.java new file mode 100644 index 000000000..fe896a055 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/activator/CoreBundleActivator.java @@ -0,0 +1,71 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. 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.tcf.te.runtime.concurrent.activator;
+
+import org.eclipse.tcf.te.runtime.tracing.TraceHandler;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class CoreBundleActivator implements BundleActivator {
+ // The bundle context
+ private static BundleContext context;
+ // The trace handler instance
+ private static TraceHandler traceHandler;
+
+ /**
+ * Returns the bundle context
+ *
+ * @return the bundle context
+ */
+ public static BundleContext getContext() {
+ return context;
+ }
+
+ /**
+ * Convenience method which returns the unique identifier of this plugin.
+ */
+ public static String getUniqueIdentifier() {
+ if (getContext() != null && getContext().getBundle() != null) {
+ return getContext().getBundle().getSymbolicName();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the bundles trace handler.
+ *
+ * @return The bundles trace handler.
+ */
+ public static TraceHandler getTraceHandler() {
+ if (traceHandler == null) {
+ traceHandler = new TraceHandler(getUniqueIdentifier());
+ }
+ return traceHandler;
+ }
+
+ /* (non-Javadoc)
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext bundleContext) throws Exception {
+ CoreBundleActivator.context = bundleContext;
+ }
+
+ /* (non-Javadoc)
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext bundleContext) throws Exception {
+ CoreBundleActivator.context = null;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/event/ExecutorThreadNotificationListener.java b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/event/ExecutorThreadNotificationListener.java new file mode 100644 index 000000000..74229359f --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/event/ExecutorThreadNotificationListener.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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.tcf.te.runtime.concurrent.event; + +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.tcf.te.runtime.concurrent.util.ExecutorsUtil; +import org.eclipse.tcf.te.runtime.interfaces.events.IEventFireDelegate; +import org.eclipse.tcf.te.runtime.interfaces.events.IEventListener; + +/** + * Abstract notification listener implementation executing the + * notifications within the shared executor thread. + */ +public abstract class ExecutorThreadNotificationListener extends PlatformObject implements IEventListener, IEventFireDelegate { + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.runtime.interfaces.events.IEventFireDelegate#fire(java.lang.Runnable) + */ + @Override + public final void fire(Runnable runnable) { + // Force notification into the executor thread. + // + // Note: The executor thread is not identical with the display thread! + // Use ExecutorsUtil.executeInUI(runnable) to execute the runnable + // within the display thread. + ExecutorsUtil.execute(runnable); + } +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/executors/AbstractDelegatingExecutorService.java b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/executors/AbstractDelegatingExecutorService.java new file mode 100644 index 000000000..e5df0a65f --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/executors/AbstractDelegatingExecutorService.java @@ -0,0 +1,212 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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.tcf.te.runtime.concurrent.executors; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.util.NLS; +import org.eclipse.tcf.te.runtime.concurrent.activator.CoreBundleActivator; +import org.eclipse.tcf.te.runtime.concurrent.interfaces.IExecutor; +import org.eclipse.tcf.te.runtime.concurrent.nls.Messages; +import org.eclipse.tcf.te.runtime.extensions.ExecutableExtension; + +/** + * Abstract delegating execution service implementation. + */ +public abstract class AbstractDelegatingExecutorService extends ExecutableExtension implements IExecutor, ExecutorService { + // The executor service to delegate the API calls to + private ExecutorService delegate; + + // The thread pool name prefix + private String threadPoolNamePrefix; + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.runtime.extensions.ExecutableExtension#doSetInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object) + */ + @Override + public void doSetInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException { + super.doSetInitializationData(config, propertyName, data); + + if (config != null && data instanceof Map<?, ?>) { + Map<?, ?> params = (Map<?, ?>) data; + // Initialize the thread pool name prefix field by reading the + // "threadPoolNamePrefix" extension attribute if present. + threadPoolNamePrefix = (String) params.get("threadPoolNamePrefix"); //$NON-NLS-1$ + if (threadPoolNamePrefix == null || threadPoolNamePrefix.trim().length() == 0) { + threadPoolNamePrefix = ""; //$NON-NLS-1$ + } + } + + // Create the executor service delegate + this.delegate = createExecutorServiceDelegate(); + Assert.isNotNull(delegate); + } + + /** + * Returns the thread pool name prefix if specified by the extension. + * + * @return The thread pool name prefix or an empty string. + */ + public String getThreadPoolNamePrefix() { + return threadPoolNamePrefix != null ? threadPoolNamePrefix : ""; //$NON-NLS-1$ + } + + /** + * Invoked by the constructor exactly once to create the executor service + * delegate instance. + * + * @return The executor service instance and never <code>null</code>. + */ + protected abstract ExecutorService createExecutorServiceDelegate(); + + /** + * Returns the executor service delegate instance. + * + * @return The executor service delegate instance. + */ + protected final ExecutorService getExecutorServiceDelegate() { + return delegate; + } + + /** + * Log the given exception as error to the error log. + * + * @param e + * The exception or <code>null</code>. + */ + protected void logException(Throwable e) { + if (e != null) { + IStatus status = new Status( + IStatus.ERROR, + CoreBundleActivator.getUniqueIdentifier(), + NLS.bind(Messages.AbstractDelegatingExecutorService_unhandledException, + e.getLocalizedMessage()), e); + Platform.getLog(CoreBundleActivator.getContext().getBundle()).log(status); + } + } + + /* (non-Javadoc) + * @see java.util.concurrent.Executor#execute(java.lang.Runnable) + */ + @Override + public void execute(Runnable command) { + delegate.execute(command); + } + + /* (non-Javadoc) + * @see java.util.concurrent.ExecutorService#shutdown() + */ + @Override + public void shutdown() { + delegate.shutdown(); + } + + /* (non-Javadoc) + * @see java.util.concurrent.ExecutorService#shutdownNow() + */ + @Override + public List<Runnable> shutdownNow() { + return delegate.shutdownNow(); + } + + /* (non-Javadoc) + * @see java.util.concurrent.ExecutorService#isShutdown() + */ + @Override + public boolean isShutdown() { + return delegate.isShutdown(); + } + + /* (non-Javadoc) + * @see java.util.concurrent.ExecutorService#isTerminated() + */ + @Override + public boolean isTerminated() { + return delegate.isTerminated(); + } + + /* (non-Javadoc) + * @see java.util.concurrent.ExecutorService#awaitTermination(long, java.util.concurrent.TimeUnit) + */ + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return delegate.awaitTermination(timeout, unit); + } + + /* (non-Javadoc) + * @see java.util.concurrent.ExecutorService#submit(java.util.concurrent.Callable) + */ + @Override + public <T> Future<T> submit(Callable<T> task) { + return delegate.submit(task); + } + + /* (non-Javadoc) + * @see java.util.concurrent.ExecutorService#submit(java.lang.Runnable, java.lang.Object) + */ + @Override + public <T> Future<T> submit(Runnable task, T result) { + return delegate.submit(task, result); + } + + /* (non-Javadoc) + * @see java.util.concurrent.ExecutorService#submit(java.lang.Runnable) + */ + @Override + public Future<?> submit(Runnable task) { + return delegate.submit(task); + } + + /* (non-Javadoc) + * @see java.util.concurrent.ExecutorService#invokeAll(java.util.Collection) + */ + @Override + public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { + return delegate.invokeAll(tasks); + } + + /* (non-Javadoc) + * @see java.util.concurrent.ExecutorService#invokeAll(java.util.Collection, long, java.util.concurrent.TimeUnit) + */ + @Override + public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException { + return delegate.invokeAll(tasks, timeout, unit); + } + + /* (non-Javadoc) + * @see java.util.concurrent.ExecutorService#invokeAny(java.util.Collection) + */ + @Override + public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException { + return delegate.invokeAny(tasks); + } + + /* (non-Javadoc) + * @see java.util.concurrent.ExecutorService#invokeAny(java.util.Collection, long, java.util.concurrent.TimeUnit) + */ + @Override + public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return delegate.invokeAny(tasks, timeout, unit); + } +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/executors/SingleThreadedExecutorService.java b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/executors/SingleThreadedExecutorService.java new file mode 100644 index 000000000..a2f21c3b5 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/executors/SingleThreadedExecutorService.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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.tcf.te.runtime.concurrent.executors; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.eclipse.tcf.te.runtime.concurrent.factories.SingleThreadThreadFactory; +import org.eclipse.tcf.te.runtime.concurrent.interfaces.INestableExecutor; +import org.eclipse.tcf.te.runtime.concurrent.interfaces.ISingleThreadedExecutor; + +/** + * A single threaded executor service implementation. + */ +public class SingleThreadedExecutorService extends AbstractDelegatingExecutorService implements ISingleThreadedExecutor, INestableExecutor { + + /** + * A single threaded executor implementation. + */ + protected class SingleThreadedExecutor extends ThreadPoolExecutor implements INestableExecutor { + // The current nesting depth + private final AtomicInteger currentNestingDepth = new AtomicInteger(0); + + /** + * Constructor. + * + * @param threadFactory + * The thread factory instance. Must not be <code>null</code>. + * + * @throws NullPointerException + * if threadFactory is <code>null</code>. + */ + public SingleThreadedExecutor(ThreadFactory threadFactory) { + this(threadFactory, new LinkedBlockingQueue<Runnable>()); + } + + /** + * Constructor. + * <p> + * Private constructor to catch the work queue instance passed into the + * {@link ThreadPoolExecutor} constructor. + * + * @param threadFactory + * The thread factory instance. Must not be <code>null</code>. + * @param workQueue + * The work queue instance. Must not be <code>null</code>. + */ + private SingleThreadedExecutor(ThreadFactory threadFactory, BlockingQueue<Runnable> workQueue) { + super(1, 1, 0L, TimeUnit.NANOSECONDS, workQueue, threadFactory); + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.runtime.concurrent.interfaces.INestableExecutor#getMaxDepth() + */ + @Override + public int getMaxDepth() { + return 1; + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.runtime.concurrent.interfaces.INestableExecutor#readAndExecute() + */ + @Override + public boolean readAndExecute() { + // Method is callable from the executor thread only + if (!isExecutorThread()) { + throw new IllegalStateException("Must be called from within the executor thread!"); //$NON-NLS-1$ + } + + BlockingQueue<Runnable> queue = getQueue(); + + // If the work queue is empty, there is nothing to do + if (!queue.isEmpty()) { + // Work queue not empty, check if we reached the maximum nesting + // depth + if (currentNestingDepth.get() >= getMaxDepth()) { + throw new IllegalStateException("Maximum nesting depth exceeded!"); //$NON-NLS-1$ + } + + // Get the next work item to do + Runnable runnable = null; + try { + // Double check that the queue is not empty, we desire to + // avoid + // blocking here! + if (!queue.isEmpty()) { + runnable = queue.take(); + } + } catch (InterruptedException e) { /* ignored on purpose */ } + + if (runnable != null) { + // Increase the nesting depth + currentNestingDepth.incrementAndGet(); + try { + // Execute the runnable + runnable.run(); + } finally { + // Decrease nesting depth + currentNestingDepth.decrementAndGet(); + } + } + } + + return !queue.isEmpty(); + } + + /* (non-Javadoc) + * @see java.util.concurrent.ThreadPoolExecutor#afterExecute(java.lang.Runnable, java.lang.Throwable) + */ + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + if (t != null) + logException(t); + } + } + + // Internal reference to the one shot thread factory instance + private SingleThreadThreadFactory threadFactory; + + /** + * Constructor. + */ + public SingleThreadedExecutorService() { + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.runtime.concurrent.executors.AbstractDelegatingExecutorService#createExecutorServiceDelegate() + */ + @Override + protected ExecutorService createExecutorServiceDelegate() { + threadFactory = new SingleThreadThreadFactory(getThreadPoolNamePrefix()); + return new SingleThreadedExecutor(threadFactory); + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.runtime.concurrent.interfaces.ISingleThreadedExecutor#isExecutorThread() + */ + @Override + public final boolean isExecutorThread() { + return isExecutorThread(Thread.currentThread()); + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.runtime.concurrent.interfaces.ISingleThreadedExecutor#isExecutorThread(java.lang.Thread) + */ + @Override + public final boolean isExecutorThread(Thread thread) { + if (thread != null && threadFactory != null) { + return thread.equals(threadFactory.getThread()); + } + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.runtime.concurrent.interfaces.INestableExecutor#getMaxDepth() + */ + @Override + public int getMaxDepth() { + if (!(getExecutorServiceDelegate() instanceof INestableExecutor)) { + throw new UnsupportedOperationException("Executor service delegate must implement INestableExecutor"); //$NON-NLS-1$ + } + return ((INestableExecutor) getExecutorServiceDelegate()).getMaxDepth(); + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.runtime.concurrent.interfaces.INestableExecutor#readAndExecute() + */ + @Override + public boolean readAndExecute() { + if (!(getExecutorServiceDelegate() instanceof INestableExecutor)) { + throw new UnsupportedOperationException("Executor service delegate must implement INestableExecutor"); //$NON-NLS-1$ + } + return ((INestableExecutor) getExecutorServiceDelegate()).readAndExecute(); + } +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/factories/SingleThreadThreadFactory.java b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/factories/SingleThreadThreadFactory.java new file mode 100644 index 000000000..3cf533f25 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/factories/SingleThreadThreadFactory.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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.tcf.te.runtime.concurrent.factories; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +import org.eclipse.core.runtime.Assert; + +/** + * A thread factory implementation creating a single thread only. + */ +public class SingleThreadThreadFactory implements ThreadFactory { + private final ThreadGroup threadGroup; + private final String threadName; + private Thread thread; + + private final AtomicInteger threadNumber = new AtomicInteger(1); + + /** + * Constructor. + * + * @param namePrefix + * The name prefix to name the created threads. Must not be + * <code>null</code>. + */ + public SingleThreadThreadFactory(String namePrefix) { + Assert.isNotNull(namePrefix); + + // Determine the thread group. Use the security manager if available. + this.threadGroup = (System.getSecurityManager() != null) ? System.getSecurityManager().getThreadGroup() : Thread.currentThread().getThreadGroup(); + // Set the thread name prefix + this.threadName = ("".equals(namePrefix.trim()) ? "Executor" : namePrefix) + " - " + threadNumber.getAndIncrement(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + /* + * (non-Javadoc) + * + * @see java.util.concurrent.ThreadFactory#newThread(java.lang.Runnable) + */ + @Override + public Thread newThread(Runnable r) { + // The thread can be created on once. If called a second time, + // this factory cannot create any additional threads. + if (thread != null) return null; + + // Create the thread with the desired name and the current thread number + thread = new Thread(threadGroup, r, threadName); + thread.setDaemon(false); + thread.setPriority(Thread.NORM_PRIORITY); + + // Return the thread + return thread; + } + + /** + * Returns the single created thread instance or <code>null</code> if + * {@link #newThread(Runnable)} have not been called yet. + * + * @return The single created thread instance or <code>null</code>. + */ + public Thread getThread() { + return thread; + } +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/interfaces/IExecutor.java b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/interfaces/IExecutor.java new file mode 100644 index 000000000..9311c5537 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/interfaces/IExecutor.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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.tcf.te.runtime.concurrent.interfaces; + +import java.util.concurrent.Executor; + +import org.eclipse.tcf.te.runtime.interfaces.extensions.IExecutableExtension; + +/** + * Execution interface declaration. + */ +public interface IExecutor extends Executor, IExecutableExtension { + +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/interfaces/IExecutorUtilDelegate.java b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/interfaces/IExecutorUtilDelegate.java new file mode 100644 index 000000000..7292f40da --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/interfaces/IExecutorUtilDelegate.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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.tcf.te.runtime.concurrent.interfaces; + +import org.eclipse.tcf.te.runtime.interfaces.extensions.IExecutableExtension; + +/** + * Executor utility delegate interface declaration. + */ +public interface IExecutorUtilDelegate extends IExecutableExtension { + + /** + * Returns if or if not the current thread is an executor thread handled by + * this executor utility wait and dispatch delegate. + * + * @return <code>True</code> if the current thread is handled, + * <code>false</code> otherwise. + */ + public boolean isHandledExecutorThread(); + + /** + * Reads an event from the handled executors event queue, dispatches it + * appropriately, and returns <code>true</code> if there is potentially more + * work to do, or <code>false</code> if the caller can sleep until another + * event is placed on the event queue. + * + * @return <code>True</code> if there is potentially more work to do, + * <code>false</code> otherwise. + */ + public boolean readAndDispatch(); +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/interfaces/INestableExecutor.java b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/interfaces/INestableExecutor.java new file mode 100644 index 000000000..af7171f9e --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/interfaces/INestableExecutor.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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.tcf.te.runtime.concurrent.interfaces; + +import java.util.concurrent.Executor; + +/** + * Nestable execution interface declaration. + */ +public interface INestableExecutor extends Executor { + + /** + * Returns the maximum allowed nesting depth. If this methods returns an + * integer value <= 0, nesting is disabled. + * + * @return The maximum allowed nesting depth or 0 to disable nesting. + */ + public int getMaxDepth(); + + /** + * Reads the next command from the task queue and execute it if the maximum + * allowed nesting depth has not been exceeded. If the maximum nesting depth + * has been reached, the method will throw an {@link IllegalStateException}. + * + * @return <code>True</code> if there is potentially more work to do, or + * <code>false</code> if the caller can sleep until another event is + * placed on the task queue. + */ + public boolean readAndExecute(); +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/interfaces/ISingleThreadedExecutor.java b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/interfaces/ISingleThreadedExecutor.java new file mode 100644 index 000000000..5ce6ab275 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/interfaces/ISingleThreadedExecutor.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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.tcf.te.runtime.concurrent.interfaces; + +import java.util.concurrent.Executor; + +/** + * Single threaded execution service interface declaration. + */ +public interface ISingleThreadedExecutor extends Executor { + + /** + * Returns if or if not the current thread is identical to the executor + * thread. + * + * @return <code>True</code> if the current thread is the executor thread, + * <code>false</code> otherwise. + */ + public boolean isExecutorThread(); + + /** + * Returns if or if not the given thread is identical to the executor + * thread. + * + * @param thread + * The thread or <code>null</code>. + * @return <code>True</code> if the current thread is the executor thread, + * <code>false</code> otherwise. + */ + public boolean isExecutorThread(Thread thread); +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/nls/Messages.java b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/nls/Messages.java new file mode 100644 index 000000000..652586645 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/nls/Messages.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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.tcf.te.runtime.concurrent.nls; + +import org.eclipse.osgi.util.NLS; + +/** + * Target Explorer Concurrent Runtime plugin externalized strings management. + */ +public class Messages extends NLS { + + // The plug-in resource bundle name + private static final String BUNDLE_NAME = "org.eclipse.tcf.te.runtime.concurrent.nls.Messages"; //$NON-NLS-1$ + + /** + * Static constructor. + */ + static { + // Load message values from bundle file + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + // **** Declare externalized string id's down here ***** + + public static String AbstractDelegatingExecutorService_unhandledException; +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/nls/Messages.properties b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/nls/Messages.properties new file mode 100644 index 000000000..6e7b09de1 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/nls/Messages.properties @@ -0,0 +1,6 @@ +# +# org.eclipse.tcf.te.runtime.concurrent +# Externalized Strings. +# + +AbstractDelegatingExecutorService_unhandledException=Unhandled exception caught in executor: {0} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/util/ExecutorsUtil.java b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/util/ExecutorsUtil.java new file mode 100644 index 000000000..f07026c03 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.runtime.concurrent/src/org/eclipse/tcf/te/runtime/concurrent/util/ExecutorsUtil.java @@ -0,0 +1,404 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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.tcf.te.runtime.concurrent.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.tcf.te.runtime.concurrent.Executors; +import org.eclipse.tcf.te.runtime.concurrent.interfaces.IExecutorUtilDelegate; +import org.eclipse.tcf.te.runtime.concurrent.interfaces.INestableExecutor; +import org.eclipse.tcf.te.runtime.concurrent.interfaces.ISingleThreadedExecutor; +import org.eclipse.tcf.te.runtime.extensions.AbstractExtensionPointManager; +import org.eclipse.tcf.te.runtime.extensions.ExecutableExtensionProxy; +import org.eclipse.tcf.te.runtime.interfaces.IConditionTester; + + +/** + * Utility class to provide helper methods to execute tasks at + * a executor service asynchronous and synchronous. + */ +public final class ExecutorsUtil { + + /** + * Execution utility wait and dispatch utility extension point manager. + */ + protected static class ExecutorUtilDelegateExtensionPointManager extends AbstractExtensionPointManager<IExecutorUtilDelegate> { + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.runtime.extensions.AbstractExtensionPointManager#getExtensionPointId() + */ + @Override + protected String getExtensionPointId() { + return "org.eclipse.tcf.te.runtime.concurrent.executorUtilDelegates"; //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.runtime.extensions.AbstractExtensionPointManager#getConfigurationElementName() + */ + @Override + protected String getConfigurationElementName() { + return "executorUtilDelegate"; //$NON-NLS-1$ + } + + /** + * Returns the list of all contributed executor utility delegates. + * + * @return The list of contributed executor utility delegates, or an + * empty array. + */ + public IExecutorUtilDelegate[] getExecutorUtilDelegates() { + List<IExecutorUtilDelegate> contributions = new ArrayList<IExecutorUtilDelegate>(); + Collection<ExecutableExtensionProxy<IExecutorUtilDelegate>> proxies = getExtensions().values(); + for (ExecutableExtensionProxy<IExecutorUtilDelegate> proxy : proxies) { + if (proxy.getInstance() != null&& !contributions.contains(proxy.getInstance())) { + contributions.add(proxy.getInstance()); + } + } + + return contributions.toArray(new IExecutorUtilDelegate[contributions.size()]); + } + + /** + * Returns the executor utility delegate identified by its unique id. If + * no executor utility delegate with the specified id is registered, + * <code>null</code> is returned. + * + * @param id + * The unique id of the executor utility delegate. Must not + * be <code>null</code> + * @param newInstance + * Specify <code>true</code> to get a new executor utility + * delegate instance, <code>false</code> otherwise. + * + * @return The executor instance or <code>null</code>. + */ + public IExecutorUtilDelegate getExecutorUtilDelegate(String id, boolean newInstance) { + Assert.isNotNull(id); + + IExecutorUtilDelegate executorUtilDelegate = null; + if (getExtensions().containsKey(id)) { + ExecutableExtensionProxy<IExecutorUtilDelegate> proxy = getExtensions().get(id); + // Get the extension instance + executorUtilDelegate = newInstance ? proxy.newInstance() : proxy.getInstance(); + } + + return executorUtilDelegate; + } + } + + // Reference to the executor service extension point manager + private final static ExecutorUtilDelegateExtensionPointManager EXTENSION_POINT_MANAGER = new ExecutorUtilDelegateExtensionPointManager(); + + // Reference to the used executor service. + private final static ISingleThreadedExecutor EXECUTOR; + // Reference to the used UI executor service (might be null if not available) + private final static ISingleThreadedExecutor UI_EXECUTOR; + + /** + * Static constructor. + */ + static { + EXECUTOR = (ISingleThreadedExecutor) Executors.getSharedExecutor("org.eclipse.tcf.te.runtime.concurrent.executors.singleThreaded"); //$NON-NLS-1$ + Assert.isNotNull(EXECUTOR); + UI_EXECUTOR = (ISingleThreadedExecutor) Executors.getSharedExecutor("org.eclipse.tcf.te.ui.executors.SWTDisplay"); //$NON-NLS-1$ + } + + /** + * Shutdown the executor service used. + */ + public static void shutdown() { + if (EXECUTOR instanceof ExecutorService) { + ((ExecutorService) EXECUTOR).shutdown(); + } + if (UI_EXECUTOR instanceof ExecutorService) { + ((ExecutorService) UI_EXECUTOR).shutdown(); + } + } + + /** + * Checks if the current thread is identical with the executor thread. + * + * @return <code>True</code> if the current thread is the executor thread, + * <code>false</code> otherwise. + */ + public static boolean isExecutorThread() { + return EXECUTOR != null ? EXECUTOR.isExecutorThread() : false; + } + + /** + * Checks if the current thread is identical with the UI executor thread. + * + * @return <code>True</code> if the current thread is the UI executor + * thread, <code>false</code> otherwise. + */ + public static boolean isUIExecutorThread() { + return UI_EXECUTOR != null ? UI_EXECUTOR.isExecutorThread() : false; + } + + /** + * Schedule the given {@link Runnable} for invocation within the used + * executor thread. + * + * @param runnable + * The <code>java.lang.Runnable</code> to execute within the + * executor thread. + */ + public static void execute(Runnable runnable) { + if (runnable != null) { + if (EXECUTOR instanceof ExecutorService) { + if (!((ExecutorService) EXECUTOR).isShutdown() + && !((ExecutorService) EXECUTOR).isTerminated()) { + EXECUTOR.execute(runnable); + } + } else { + EXECUTOR.execute(runnable); + } + } + } + + + /** + * Schedule the given {@link Runnable} for invocation within the used + * executor thread and blocks the caller until the runnable got executed. + * <p> + * <b>Note:</b> The method is using {@link #wait()} to block the calling + * thread. Therefore the method cannot be called from within + * the executor thread itself. + * + * @param runnable + * The <code>java.lang.Runnable</code> to execute within the + * executor thread. + */ + public static void executeWait(final Runnable runnable) { + Assert.isTrue(!EXECUTOR.isExecutorThread()); + if (runnable == null) return; + + final AtomicBoolean invoked = new AtomicBoolean(false); + + // Wrap the original runnable in another runnable + // to notify ourself + Runnable r = new Runnable() { + @Override + public void run() { + try { + runnable.run(); + } finally { + invoked.set(true); + synchronized(runnable) { + runnable.notifyAll(); + } + } + } + }; + + if (EXECUTOR instanceof ExecutorService) { + if (!((ExecutorService) EXECUTOR).isShutdown() + && !((ExecutorService) EXECUTOR).isTerminated()) { + EXECUTOR.execute(r); + } + } else { + EXECUTOR.execute(r); + } + + synchronized(runnable) { + try { + if (!invoked.get()) runnable.wait(); + } catch (InterruptedException e) { + /* ignored on purpose */ + } + } + } + + /** + * Schedule the given {@link Runnable} to run the current platform display + * thread and blocks the caller until the runnable got executed. + * + * @param runnable + * The <code>java.lang.Runnable</code> to execute within the + * UI thread. + */ + public static void executeInUI(Runnable runnable) { + if (runnable != null) { + if (UI_EXECUTOR instanceof ExecutorService) { + if (!((ExecutorService) UI_EXECUTOR).isShutdown() + && !((ExecutorService) UI_EXECUTOR).isTerminated()) { + UI_EXECUTOR.execute(runnable); + } + } else { + if (UI_EXECUTOR != null) { + UI_EXECUTOR.execute(runnable); + } + } + } + } + + /** + * Schedule the given {@link Runnable} to run the current platform display + * thread and blocks the caller until the runnable got executed. + * + * @param runnable + * The <code>java.lang.Runnable</code> to execute within the + * UI thread. + */ + public static void executeInUIWait(final Runnable runnable) { + if (runnable == null) return; + + final AtomicBoolean invoked = new AtomicBoolean(false); + + // Wrap the original runnable in another runnable + // to set the invoked flag + Runnable r = new Runnable() { + @Override + public void run() { + try { + runnable.run(); + } finally { + invoked.set(true); + } + } + }; + + if (UI_EXECUTOR instanceof ExecutorService) { + if (!((ExecutorService) UI_EXECUTOR).isShutdown() + && !((ExecutorService) UI_EXECUTOR).isTerminated()) { + UI_EXECUTOR.execute(r); + } + } else { + if (UI_EXECUTOR != null) { + UI_EXECUTOR.execute(r); + } else { + invoked.set(true); + } + } + + waitAndExecute(0, new IConditionTester() { + @Override + public boolean isConditionFulfilled() { + return invoked.get(); + } + @Override + public void cleanup() { + } + }); + } + + /** + * Waits either for the given condition tester to signal that the condition, + * the caller want's to wait for, has been completely fulfilled or till the + * timeout runs out. If the specified condition tester is <code>null</code>, + * the method will always wait till the timeout occurs. In case + * <code>timeout == 0</code> and <code>conditionTester == null</code>, the + * method returns immediately with the return value <code>true</code>! + * + * @param timeout + * The timeout to wait in milliseconds. <code>0</code> means + * infinite wait time! + * @param conditionTester + * The condition tester to use for checking the interrupt + * condition. + * + * @return <code>false</code> if the exit reason if that the waiting + * condition has been fulfilled, <code>true</code> if the exit + * reason is the timeout! + */ + public static boolean waitAndExecute(final long timeout, final IConditionTester conditionTester) { + // both parameter are null, return immediately! + if (conditionTester == null && timeout == 0) + return true; + + // we assume that the exit reason will be the timeout + boolean exitReason = true; + + // Remember the executors utility delegate down the road. As long + // we don't leave the waitAndExecute method, the thread cannot change. + IExecutorUtilDelegate lastDelegate = null; + + // Remember the start time to calculate the timeout + final long startTime = System.currentTimeMillis(); + // keep going till either the condition tester or the timeout will + // break the loop! + while (true) { + if (conditionTester != null && conditionTester.isConditionFulfilled()) { + // the exit reason is the condition tester! + exitReason = false; + break; + } + if (timeout != 0 && ((System.currentTimeMillis() - startTime) >= timeout)) { + // timeout occurred, just break the loop + break; + } + // none of the break conditions are fulfilled, so wait a little bit + // before testing again. + if (isExecutorThread()) { + // We are in the executor thread. Keep the command dispatching running. + if (EXECUTOR instanceof INestableExecutor) { + ((INestableExecutor) EXECUTOR).readAndExecute(); + Thread.yield(); + } else { + throw new IllegalStateException("waitAndExecute called from within a non-nestable executor service!"); //$NON-NLS-1$ + } + } + // Check if we are in the UI executor thread + else if (isUIExecutorThread()) { + // We are in the executor thread. Keep the command dispatching + // running. + if (UI_EXECUTOR instanceof INestableExecutor) { + ((INestableExecutor) UI_EXECUTOR).readAndExecute(); + Thread.yield(); + } else { + throw new IllegalStateException("waitAndExecute called from within a non-nestable UI executor service!"); //$NON-NLS-1$ + } + } + // Check if we have a delegate contribution which is handling + // the current thread. + else { + boolean foundHandlingDelegate = false; + + if (lastDelegate == null) { + // Get all registered delegates + IExecutorUtilDelegate[] delegates = EXTENSION_POINT_MANAGER.getExecutorUtilDelegates(); + for (IExecutorUtilDelegate delegate : delegates) { + // Does the delegate handles the current thread? + if (delegate.isHandledExecutorThread()) { + foundHandlingDelegate = true; + lastDelegate = delegate; + // Read and dispatch one event + delegate.readAndDispatch(); + break; + } + } + } else { + foundHandlingDelegate = true; + // Read and dispatch one event + lastDelegate.readAndDispatch(); + } + + if (!foundHandlingDelegate) { + // Not in any executor thread, put the current thread to sleep + try { + Thread.sleep(10); + } catch (InterruptedException e) { /* ignored on purpose */ } + } + } + } + + // give the condition tester the chance to cleanup + if (conditionTester != null) { + conditionTester.cleanup(); + } + + return exitReason; + } +} |