/*******************************************************************************
* Copyright (c) 2016 Bruno Medeiros and other Contributors.
* 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:
* Bruno Medeiros - initial API and implementation
*******************************************************************************/
package melnorme.utilbox.concurrency;
import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue;
import java.util.concurrent.FutureTask;
/**
* A task that can be cancelled before, or while it is running.
* If cancelled while it is running, the running thread is interrupted.
*
* - This is related to {@link FutureTask}, achieves part of what it does.
* I do wonder if we should be using {@link FutureTask} instead as it might be more robust than rolling our own.
* (although we don't need the whole of {@link FutureTask}, just a small subset)
*
*/
public abstract class CancellableTask implements ICancellableTask {
private boolean cancelled = false;
private Thread runningThread;
private boolean executed = false;
protected final ICancelMonitor cm = this::isCancelled;
public synchronized boolean isCancelled() {
return cancelled;
}
@Override
public boolean canExecute() {
return !hasExecuted();
}
public synchronized boolean hasExecuted() {
return executed;
}
public void cancel() {
tryCancel();
}
@Override
public synchronized boolean tryCancel() {
if(cancelled) {
return false;
}
cancelled = true;
if(runningThread != null) {
runningThread.interrupt();
}
return true;
}
@Override
public void run() {
synchronized(this) {
if(isCancelled()) {
return;
}
// Can only run once
markExecuted();
assertTrue(runningThread == null);
runningThread = Thread.currentThread();
}
String taskDisplayName = getTaskDisplayName();
if(taskDisplayName == null) {
doRun();
} else {
String originalName = runningThread.getName();
try {
runningThread.setName(originalName + " >> " + taskDisplayName);
doRun();
} finally {
runningThread.setName(originalName);
}
}
synchronized(this) {
// make it null so that the thread can no longer be interrupted
runningThread = null;
Thread.interrupted();
}
}
public void markExecuted() {
assertTrue(executed == false); // Check that run is only called once
executed = true;
}
public String getTaskDisplayName() {
return null;
}
protected abstract void doRun();
}