/*******************************************************************************
* Copyright (c) 2011, 2014 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 {
while (!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;
}
}