package statalign.base.thread;
/**
* Objects of this class can be used in place of a Thread object if it is intended that
* they can be stopped or suspended "softly" by an external thread. This means that if an
* external thread requests termination of this thread (by calling stopSoft()),
* execution will continue up to the first "stopping point", i.e. the first call of
* stoppable() from within this thread.
* At this point, a StoppedException will be thrown, and the StoppableThread will be given
* the chance to catch it at an arbitrary point and do up before termination.
*
* In every other aspect, a StoppableThread is like a Thread, e.g. you should override
* run() and place your "softly stoppable" code there. But you must declare your run()
* method <code>synchronized</code> for most of the above to work properly.
*
* The easiest way to define "stopping points" and "pausing points" in your own classes
* is to use Stoppable as the ancestor and call its stoppable() and pausable() methods from
* your methods.
*
* @author novak
*
*/
public class StoppableThread extends Thread {
volatile boolean pausing;
volatile boolean stopping;
/**
* Should be called from within the thread to define a "pausing point". If an external
* thread has previously requested the suspension of the thread (by calling suspendSoft()),
* this method will block until the external thread calls resumeSoft() or stopSoft().
*/
void pausable() {
while(pausing)
try {
wait();
} catch(InterruptedException e) {
}
}
/**
* Should be called from within the thread to define a "stopping point". This implies
* a "pausing point" (see pausable()). If an external thread has previously requested the
* termination of the thread (by calling stopSoft()), a StoppedException will be thrown
* which can be caught at any point within this thread to make up before termination.
*/
void stoppable() throws StoppedException {
pausable();
if(stopping)
throw new StoppedException();
}
/**
* Can be called by an external thread to request suspension of this thread. The
* method will not return until execution of this thread reaches a "pausing" or
* "stopping point", i.e. until pausable() or stoppable() is called from within this
* thread. At that point, this thread will be suspended until resumeSoft() or stopSoft()
* is called by the external thread.
*
* Compare deprecated "hard" suspend: Thread.suspend()
*/
public void suspendSoft() {
pausing = true;
synchronized(this) {}
}
/**
* Calls suspendSoft() or resumeSoft(), depending on the current state of this thread.
*/
public void pauseToggleSoft() {
if(pausing)
resumeSoft();
else
suspendSoft();
}
/**
* Can be called by an external thread to request suspension of this thread. Unlike
* pauseSoft(), this method is non-blocking, but otherwise has the same effect.
*/
public void pauseNoWait() {
pausing = true;
}
/**
* Resumes execution of this thread if it has been previously paused by
* pauseSoft(). To be called by an external thread. Non-blocking, even if pauseNoWait()
* was used and this thread has not yet reached a "pausing point".
*
* Compare deprecated "hard" resume: Thread.resume()
*/
public synchronized void resumeSoft() {
pausing = false;
notify();
}
/**
* Can be called by an external thread to request termination of this thread. This
* method will not return until execution of this thread reaches a
* "stopping point", i.e. until stoppable() is called from within this
* thread.
* Can also be used if this thread has been suspended by suspendSoft().
*
* Compare deprecated "hard" stop: Thread.stop()
*/
public void stopSoft() {
stopping = true;
resumeSoft();
}
/**
* Similar to stopSoft() but non-blocking, and also will not wake up this thread if
* it is suspended. In that case, use (non-blocking) resumeSoft() first.
*/
public void stopNoWait() {
pausing = false;
stopping = true;
}
}