mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-16 21:45:22 +02:00
[213635] Added name to DSF executor.
This commit is contained in:
parent
2b3d31bb31
commit
0ce3cca4da
4 changed files with 267 additions and 142 deletions
|
@ -0,0 +1,232 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2007 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.dd.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.dd.dsf.concurrent.DefaultDsfExecutor;
|
||||||
|
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.SWTException;
|
||||||
|
import org.eclipse.swt.widgets.Display;
|
||||||
|
|
||||||
|
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.
|
||||||
|
* @param display Display to create an executor for.
|
||||||
|
* @return The new (or re-used) executor.
|
||||||
|
*/
|
||||||
|
public static DsfExecutor 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
return new Callable<V>() {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public V call() throws Exception {
|
||||||
|
final Object[] v = new Object[1];
|
||||||
|
final Throwable[] e = new Throwable[1];
|
||||||
|
|
||||||
|
try {
|
||||||
|
fDisplay.syncExec(new Runnable() {
|
||||||
|
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 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) {
|
||||||
|
return new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
final Throwable[] e = new Throwable[1];
|
||||||
|
try {
|
||||||
|
fDisplay.syncExec(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
runnable.run();
|
||||||
|
} 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];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isShutdown() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return super.isShutdown();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,138 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2007 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.dd.dsf.ui.concurrent;
|
|
||||||
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.ScheduledFuture;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import org.eclipse.dd.dsf.concurrent.DefaultDsfExecutor;
|
|
||||||
import org.eclipse.swt.widgets.Display;
|
|
||||||
|
|
||||||
public class SWTDispatchDsfExecutor extends DefaultDsfExecutor
|
|
||||||
{
|
|
||||||
|
|
||||||
public SWTDispatchDsfExecutor()
|
|
||||||
{
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
private <V> Callable<V> createSWTDispatchCallable(final Callable<V> callable)
|
|
||||||
{
|
|
||||||
return new Callable<V>()
|
|
||||||
{
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public V call() throws Exception
|
|
||||||
{
|
|
||||||
final Object[] v = new Object[1];
|
|
||||||
final Throwable[] e = new Throwable[1];
|
|
||||||
|
|
||||||
Display.getDefault().syncExec(new Runnable()
|
|
||||||
{
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
v[0] = callable.call();
|
|
||||||
}
|
|
||||||
catch(Throwable exception)
|
|
||||||
{
|
|
||||||
e[0] = exception;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(e[0] instanceof RuntimeException)
|
|
||||||
throw (RuntimeException) e[0];
|
|
||||||
else if(e[0] instanceof Exception)
|
|
||||||
throw (Exception) e[0];
|
|
||||||
|
|
||||||
return (V) v[0];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private Runnable createSWTDispatchRunnable(final Runnable runnable)
|
|
||||||
{
|
|
||||||
return new Runnable()
|
|
||||||
{
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
final Throwable[] e = new Throwable[1];
|
|
||||||
|
|
||||||
Display.getDefault().syncExec(new Runnable()
|
|
||||||
{
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
runnable.run();
|
|
||||||
}
|
|
||||||
catch(Throwable exception)
|
|
||||||
{
|
|
||||||
e[0] = exception;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(e[0] instanceof RuntimeException)
|
|
||||||
throw (RuntimeException) e[0];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <V> ScheduledFuture<V> schedule(final Callable<V> callable, long delay,
|
|
||||||
TimeUnit unit) {
|
|
||||||
return super.schedule(createSWTDispatchCallable(callable), delay, unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ScheduledFuture<?> schedule(Runnable command, long delay,
|
|
||||||
TimeUnit unit) {
|
|
||||||
return super.schedule(createSWTDispatchRunnable(command), delay, unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
|
|
||||||
long initialDelay, long period, TimeUnit unit) {
|
|
||||||
return super.scheduleAtFixedRate(createSWTDispatchRunnable(command), initialDelay, period, unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
|
|
||||||
long initialDelay, long delay, TimeUnit unit) {
|
|
||||||
return super.scheduleWithFixedDelay(createSWTDispatchRunnable(command), initialDelay, delay, unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(Runnable command) {
|
|
||||||
super.execute(createSWTDispatchRunnable(command));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> Future<T> submit(Callable<T> callable) {
|
|
||||||
return super.submit(createSWTDispatchCallable(callable));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> Future<T> submit(Runnable command, T result) {
|
|
||||||
return super.submit(createSWTDispatchRunnable(command), result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<?> submit(Runnable command) {
|
|
||||||
return super.submit(createSWTDispatchRunnable(command));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,2 +1,3 @@
|
||||||
org.eclipse.dd.dsf/debug = false
|
org.eclipse.dd.dsf/debug = false
|
||||||
org.eclipse.dd.dsf/debug/executor = false
|
org.eclipse.dd.dsf/debug/executor = false
|
||||||
|
org.eclipse.dd.dsf/debug/executorName =
|
||||||
|
|
|
@ -36,18 +36,43 @@ import org.eclipse.dd.dsf.DsfPlugin;
|
||||||
public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor
|
public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor
|
||||||
implements DsfExecutor
|
implements DsfExecutor
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Instance counter for DSF executors. Used in the executor's thread name.
|
||||||
|
*/
|
||||||
|
private static int fgInstanceCounter = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the executor, used in the executor's thread name.
|
||||||
|
*/
|
||||||
|
private String fName;
|
||||||
|
|
||||||
/** Thread factory that creates the single thread to be used for this executor */
|
/** Thread factory that creates the single thread to be used for this executor */
|
||||||
static class DsfThreadFactory implements ThreadFactory {
|
static class DsfThreadFactory implements ThreadFactory {
|
||||||
|
private String fThreadName;
|
||||||
|
DsfThreadFactory(String name) {
|
||||||
|
fThreadName = name;
|
||||||
|
}
|
||||||
|
|
||||||
Thread fThread;
|
Thread fThread;
|
||||||
public Thread newThread(Runnable r) {
|
public Thread newThread(Runnable r) {
|
||||||
assert fThread == null; // Should be called only once.
|
assert fThread == null; // Should be called only once.
|
||||||
fThread = new Thread(new ThreadGroup("DSF Thread Group"), r, "DSF Dispatch Thread", 0); //$NON-NLS-1$//$NON-NLS-2$
|
fThread = new Thread(new ThreadGroup(fThreadName), r, fThreadName, 0);
|
||||||
return fThread;
|
return fThread;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefaultDsfExecutor() {
|
public DefaultDsfExecutor() {
|
||||||
super(1, new DsfThreadFactory());
|
this("DSF Executor"); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new DSF Executor with the given name.
|
||||||
|
* @param name Name used to create executor's thread.
|
||||||
|
*/
|
||||||
|
public DefaultDsfExecutor(String name) {
|
||||||
|
super(1, new DsfThreadFactory(name + " - " + fgInstanceCounter++)); //$NON-NLS-1$
|
||||||
|
fName = name;
|
||||||
|
|
||||||
if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) {
|
if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) {
|
||||||
// If tracing, pre-start the dispatch thread, and add it to the map.
|
// If tracing, pre-start the dispatch thread, and add it to the map.
|
||||||
prestartAllCoreThreads();
|
prestartAllCoreThreads();
|
||||||
|
@ -84,10 +109,13 @@ public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor
|
||||||
// Utilities used for tracing.
|
// Utilities used for tracing.
|
||||||
//
|
//
|
||||||
static boolean DEBUG_EXECUTOR = false;
|
static boolean DEBUG_EXECUTOR = false;
|
||||||
|
static String DEBUG_EXECUTOR_NAME = "";
|
||||||
static boolean ASSERTIONS_ENABLED = false;
|
static boolean ASSERTIONS_ENABLED = false;
|
||||||
static {
|
static {
|
||||||
DEBUG_EXECUTOR = DsfPlugin.DEBUG && "true".equals( //$NON-NLS-1$
|
DEBUG_EXECUTOR = DsfPlugin.DEBUG && "true".equals( //$NON-NLS-1$
|
||||||
Platform.getDebugOption("org.eclipse.dd.dsf/debug/executor")); //$NON-NLS-1$
|
Platform.getDebugOption("org.eclipse.dd.dsf/debug/executor")); //$NON-NLS-1$
|
||||||
|
DEBUG_EXECUTOR_NAME = DsfPlugin.DEBUG
|
||||||
|
? Platform.getDebugOption("org.eclipse.dd.dsf/debug/executorName") : ""; //$NON-NLS-1$
|
||||||
assert (ASSERTIONS_ENABLED = true) == true;
|
assert (ASSERTIONS_ENABLED = true) == true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +169,7 @@ public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor
|
||||||
fCurrentlyExecuting = this;
|
fCurrentlyExecuting = this;
|
||||||
|
|
||||||
// Write to console only if tracing is enabled (as opposed to tracing or assertions).
|
// Write to console only if tracing is enabled (as opposed to tracing or assertions).
|
||||||
if (DEBUG_EXECUTOR) {
|
if (DEBUG_EXECUTOR && ("".equals(DEBUG_EXECUTOR_NAME) || fName.equals(DEBUG_EXECUTOR_NAME))) { //$NON-NLS-1$
|
||||||
StringBuilder traceBuilder = new StringBuilder();
|
StringBuilder traceBuilder = new StringBuilder();
|
||||||
|
|
||||||
// Record the time
|
// Record the time
|
||||||
|
@ -212,7 +240,9 @@ public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor
|
||||||
fRunnable = runnable;
|
fRunnable = runnable;
|
||||||
|
|
||||||
// Check if executable wasn't executed already.
|
// Check if executable wasn't executed already.
|
||||||
if (DEBUG_EXECUTOR && fRunnable instanceof DsfExecutable) {
|
if (fRunnable instanceof DsfExecutable &&
|
||||||
|
DEBUG_EXECUTOR && ("".equals(DEBUG_EXECUTOR_NAME) || fName.equals(DEBUG_EXECUTOR_NAME))) //$NON-NLS-1$
|
||||||
|
{
|
||||||
assert !((DsfExecutable)fRunnable).getSubmitted() : "Executable was previously executed."; //$NON-NLS-1$
|
assert !((DsfExecutable)fRunnable).getSubmitted() : "Executable was previously executed."; //$NON-NLS-1$
|
||||||
((DsfExecutable)fRunnable).setSubmitted();
|
((DsfExecutable)fRunnable).setSubmitted();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue