diff --git a/common/source/java/ch/systemsx/cisd/common/concurrent/TerminableCallable.java b/common/source/java/ch/systemsx/cisd/common/concurrent/TerminableCallable.java index abc8e2fc42f08e7969485c0911275d0ae6e9d6b8..0750382f5fcc287830b714f72d2ba5267318e62a 100644 --- a/common/source/java/ch/systemsx/cisd/common/concurrent/TerminableCallable.java +++ b/common/source/java/ch/systemsx/cisd/common/concurrent/TerminableCallable.java @@ -421,7 +421,7 @@ public final class TerminableCallable<V> implements Callable<V>, ITerminable } /** - * Returns <code>true</code>, if the callable is currently running and <code>false</code> + * Returns <code>true</code> if the callable is currently running and <code>false</code> * otherwise. */ public boolean isRunning() @@ -430,7 +430,15 @@ public final class TerminableCallable<V> implements Callable<V>, ITerminable } /** - * Returns <code>true</code>, if the callable has already started running. + * Returns <code>true</code> if the callable has been (successfully) cancelled or terminated. + */ + public boolean isCancelled() + { + return threadGuard.isCancelled(); + } + + /** + * Returns <code>true</code> if the callable has already started running. */ public boolean hasStarted() { @@ -438,7 +446,7 @@ public final class TerminableCallable<V> implements Callable<V>, ITerminable } /** - * Returns <code>true</code>, if the callable has already finished running. + * Returns <code>true</code> if the callable has already finished running. */ public boolean hasFinished() { @@ -465,12 +473,14 @@ public final class TerminableCallable<V> implements Callable<V>, ITerminable /** * Cancels the callable if it is not yet running. * + * @param mayInterruptIfRunning If <code>true</code> and the callable is running, interrupt + * its thread. Otherwise, do nothing. * @return <code>true</code>, if the callable is cancelled and <code>false</code> * otherwise. */ - public boolean cancel() + public boolean cancel(boolean mayInterruptIfRunning) { - return threadGuard.cancel(); + return threadGuard.cancel(mayInterruptIfRunning); } /** diff --git a/common/source/java/ch/systemsx/cisd/common/concurrent/TerminableFuture.java b/common/source/java/ch/systemsx/cisd/common/concurrent/TerminableFuture.java index dc782131a83d1a2d2f97ff7e0895700ee372805c..ee7fea0856c129f6cbf2954324ccaa83cb8555c0 100644 --- a/common/source/java/ch/systemsx/cisd/common/concurrent/TerminableFuture.java +++ b/common/source/java/ch/systemsx/cisd/common/concurrent/TerminableFuture.java @@ -43,7 +43,9 @@ final class TerminableFuture<V> implements ITerminableFuture<V> public boolean cancel(boolean mayInterruptIfRunning) { - return delegateFuture.cancel(mayInterruptIfRunning); + final boolean canceled = delegateTerminableCallable.cancel(mayInterruptIfRunning); + delegateFuture.cancel(false); + return canceled; } public V get() throws InterruptedException, ExecutionException @@ -59,7 +61,7 @@ final class TerminableFuture<V> implements ITerminableFuture<V> public boolean isCancelled() { - return delegateFuture.isCancelled(); + return delegateTerminableCallable.isCancelled(); } public boolean isDone() diff --git a/common/source/java/ch/systemsx/cisd/common/concurrent/ThreadGuard.java b/common/source/java/ch/systemsx/cisd/common/concurrent/ThreadGuard.java index 9691503906e62c56ea536baa57dd25929c5db4f9..66a32c27c5e17001e45aca33d5e4287149fde0dc 100644 --- a/common/source/java/ch/systemsx/cisd/common/concurrent/ThreadGuard.java +++ b/common/source/java/ch/systemsx/cisd/common/concurrent/ThreadGuard.java @@ -48,6 +48,8 @@ final class ThreadGuard private ThreadGuard.State state = State.INITIAL; + private volatile boolean cancelled = false; + @SuppressWarnings("deprecation") private static void stopNow(Thread t) { @@ -172,31 +174,43 @@ final class ThreadGuard } /** - * Returns <code>true</code>, if the guard is in state running. + * Returns <code>true</code> if the guard is in state <code>RUNNING</code>. */ synchronized boolean isRunning() { return (state == State.RUNNING); } + /** + * Returns <code>true</code> if {@link #cancel(boolean)} has been called on the guard + * successfully, or when {@link #terminateAndWait(long, long)} has been called on the guard. + */ + boolean isCancelled() + { + return cancelled; + } + /** * Tries to cancel the guard, i.e. prevent it from running if it doesn't run yet. If canceling * is successful, it implies marking the guard as finished. * + * @param mayInterruptIfRunning If <code>true</code> and the guard is in state + * <code>RUNNING</code>, interrupt the thread. Otherwise, do nothing. * @return <code>true</code>, if the guard has been canceled successfully. */ - synchronized boolean cancel() + synchronized boolean cancel(boolean mayInterruptIfRunning) { if (state == State.INITIAL) { state = State.CANCELED; // Do not call markFinished() as the stopLock is not yet initialized. finishedLatch.countDown(); - return true; + cancelled = true; } else { - return false; + cancelled = mayInterruptIfRunning ? (tryInterruptAndGetThread() != null) : false; } + return cancelled; } /** @@ -234,7 +248,7 @@ final class ThreadGuard boolean terminateAndWait(long waitInterruptMillis, long timeoutMillis) throws InterruptedException { - if (cancel()) + if (cancel(false)) { return true; } @@ -242,6 +256,7 @@ final class ThreadGuard final Thread t = tryInterruptAndGetThread(); if (t != null) { + cancelled = true; if (waitForFinished(waitInterruptMillis)) { return true; diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/concurrent/TerminableCallableTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/concurrent/TerminableCallableTest.java index 33093846ac7a9dc165ddc7cad92cbbf058015bfa..06da6b0151fe035fc712065095d57742b850b64c 100644 --- a/common/sourceTest/java/ch/systemsx/cisd/common/concurrent/TerminableCallableTest.java +++ b/common/sourceTest/java/ch/systemsx/cisd/common/concurrent/TerminableCallableTest.java @@ -160,7 +160,7 @@ public class TerminableCallableTest new TestRunnable(launchLatch, milestoneLatch, Strategy.COMPLETE_IMMEDIATELY, finishLatch); final TerminableCallable<Object> callableUnderTest = TerminableCallable.create(sensor); - callableUnderTest.cancel(); + callableUnderTest.cancel(false); final Thread t = new Thread(callableUnderTest.asRunnable(), "cancel"); final AtomicReference<Throwable> uncaughtException = new AtomicReference<Throwable>(null); t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler()