org.opentcs.util.CyclicTask Maven / Gradle / Ivy
/**
* Copyright (c) The openTCS Authors.
*
* This program is free software and subject to the MIT license. (For details,
* see the licensing information (LICENSE.txt) you should have received with
* this copy of the software.)
*/
package org.opentcs.util;
import static org.opentcs.util.Assertions.checkInRange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A template for cyclic tasks.
* Subclasses only need to provide an implementation of
* runActualTask()
, which will be called until the task is
* terminated by calling terminate()
; after each call of
* runActualTask()
, a configurable delay may be inserted.
*
* @author Stefan Walter (Fraunhofer IML)
*/
public abstract class CyclicTask
implements Runnable {
/**
* This class's Logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(CyclicTask.class);
/**
* The time (in ms) that this task sleeps after each execution of
* runActualTask()
.
*/
private final long sleepTime;
/**
* A private object to safely synchronize on.
*/
private final Object syncObject = new Object();
/**
* The thread executing this task.
*/
private volatile Thread taskThread;
/**
* This task's terminated flag.
*/
private volatile boolean terminated;
/**
* Whether this task ignores interrupts occurring while sleeping between
* executions of the actual task.
*/
private volatile boolean ignoringInterrupts;
/**
* Creates a new CyclicTask.
*
* @param tSleep The time to sleep between two executions of the actual
* task (in milliseconds).
*/
public CyclicTask(final long tSleep) {
this.sleepTime = checkInRange(tSleep, 0, Long.MAX_VALUE, "tSleep");
}
/**
* Indicates whether this task has been terminated.
*
* @return true
if, and only if, this task's
* terminated flag has been set.
*/
public boolean isTerminated() {
return terminated;
}
/**
* Terminates this task before its next execution cycle.
* This method merely flags the task for termination and returns immediately.
* If the actual task is currently being executed, its execution will not be
* interrupted, but it will not be run again after finishing.
*/
public void terminate() {
synchronized (syncObject) {
if (isTerminated()) {
LOG.warn("Already terminated");
}
terminated = true;
syncObject.notify();
}
}
/**
* Terminates this task before its next execution cycle and waits for it to
* finish before returning.
* (This method waits for termination unless the calling thread is the thread
* that is executing this task. In that case, this method merely flags this
* task for termination and returns immediately.)
*/
public void terminateAndWait() {
Thread joinThread;
synchronized (syncObject) {
if (isTerminated()) {
LOG.warn("Already terminated");
return;
}
else {
joinThread = taskThread;
terminated = true;
syncObject.notify();
}
}
// Wait for the executing thread to finish - unless the end of
// execution had already been reached or the executing thread is terminating
// this task itself. (In the latter case, we would wait forever for the
// join() to return.)
if (joinThread != null && joinThread != Thread.currentThread()) {
try {
joinThread.join();
}
catch (InterruptedException exc) {
throw new IllegalStateException("Unexpectedly interrupted", exc);
}
}
}
/**
* Indicates whether this task is ignoring interrupts while it's sleeping.
*
* @return true
if, and only if, this task is ignoring interrupts
* while it's sleeping.
*/
public boolean isIgnoringInterrupts() {
return ignoringInterrupts;
}
/**
* Sets/unsets this task's flag for ignoring interrupts during sleep phases.
*
* @param ignoreInterrupts If true
, this task will ignore
* interrupts during sleep phases; if false
, the
* run()
method will throw an exception when interrupted.
*/
public void setIgnoringInterrupts(boolean ignoreInterrupts) {
ignoringInterrupts = ignoreInterrupts;
}
@Override
public void run() {
LOG.debug("method entry");
// Save the executing thread for use in terminateAndWait().
taskThread = Thread.currentThread();
// Execute the actual task until terminated.
while (!isTerminated()) {
LOG.debug("Running actual task...");
runActualTask();
// Only sleep if this task is not terminated and the sleep time is not 0.
if (!isTerminated() && sleepTime > 0) {
synchronized (syncObject) {
try {
syncObject.wait(sleepTime);
}
catch (InterruptedException exc) {
if (!isIgnoringInterrupts() || isTerminated()) {
LOG.error("Unexpectedly interrupted", exc);
throw new IllegalStateException("Unexpectedly interrupted", exc);
}
}
}
}
}
// Unset taskThread again - this should prevent problems with join()ing
// threads (in terminateAndWait()) that execute more than one task
// subsequently.
taskThread = null;
LOG.debug("end of method");
}
/**
* Defines the actual work this task should do in every cycle.
*/
protected abstract void runActualTask();
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy