Threads Module User's Guide : PART II Concurrency Packages : Chapter 3 The Threading Package : The Runnable Object Classes : Interrupting a Runnable
Interrupting a Runnable
A runnable can have its execution interrupted at the request of another thread. A runnable can also interrupt itself. An interrupted runnable must be released from an interrupt by another thread in order to resume execution. This operation is implemented by the following functions:
RWWaitStatus RWRunnable::requestInterrupt(void)
RWWaitStatus RWRunnable::requestInterrupt(unsigned long milliseconds)
void RWRunnable::releaseInterrupt(void)
void RWRunnableSelf::interrupt(void)
bool RWRunnableHandle::isInterruptRequested(void) const
bool RWRunnableSelf::serviceInterrupt(void)
Figure 12 shows the timing-sequence and state changes associated with various interrupt operations.
Avoiding Deadlock
Interrupts are used to halt execution of a thread at predictable locations within your application code. Traditionally, the suspend and resume capability of a thread API would be used to interrupt the execution of a thread. But thread suspension is asynchronous and occurs regardless of the current activity of the thread. Suspending a thread that is currently holding a mutex can result in an unrecoverable deadlock situation. Even if your code does not explicitly use mutexes or other synchronization mechanisms, other libraries, such as a compiler’s run-time library, can use these mechanisms to protect global static data.
To avoid the problems associated with thread suspension, the Threading package can synchronously interrupt a thread executing within a runnable. This is done through the interrupt mechanism, rather than suspend and resume.
Figure 12 – Interrupt operations — interactions and timing
Interrupting a Runnable from Another Thread
To interrupt a runnable from another thread, you need to call the RWRunnable function, requestInterrupt(). This function increments an interrupt count maintained within the runnable instance and waits for the runnable to interrupt.
Completing the Interrupt Request
To complete the interrupt request, the thread executing inside the runnable must call the serviceInterrupt() function provided by the RWRunnableSelf handle class. This function blocks the calling thread as long as one or more outstanding interrupt requests exist.
Prior to blocking, the interrupted thread sets the execution state to RW_THR_INTERRUPTED[8]. This state change signals acknowledgment to all of the threads that requested an interrupt. The interrupted thread remains blocked inside the serviceInterrupt() routine until signaled that all acknowledged interrupt requests have been released. The serviceInterrupt() function returns true if the caller was interrupted by the call and false otherwise.
The RWRunnable::releaseInterrupt() function unblocks the thread in an interrupted runnable. Each time the release function is called, the internal interrupt count is decremented. When the count reaches zero, the releaseInterrupt() function restores the original execution state (usually RW_THR_RUNNING[9]), thereby signaling the blocked thread to resume execution. Any attempt to call releaseInterrupt() on a runnable that is not interrupted is ignored.
Types of requestInterrupt() Functions
The Threading package has two types of requestInterrupt() functions:
One waits indefinitely for the runnable to either interrupt or exit.
One accepts a time-limit for the wait.
The requestInterrupt() functions return one of the following:
RW_THR_ABORTED if the runnable was not active or exited during the request.
RW_THR_ACQUIRED if the runnable was successfully interrupted.
RW_THR_TIMEOUT if the request was time-limited and the runnable failed to interrupt within the allotted time.
Several functions in the Threading package accept a timeout value. This value is meant to specify the amount of “wall-clock” time, in milliseconds, that a function waits before timing out.
NOTE >> In some situations, functions that accept a timeout value actually measure duration by the amount of CPU time used, not by the elapsed wall-clock time.
This may result in longer timeout periods than are expected or intended. In these situations, the amount of delay is directly proportional to the percentage of available CPU time granted to the process.
Shutting Down a Continuous Process
An interrupt can be used to shutdown a continuous, iterative process by using the serviceInterrupt() routine to control the process iteration, as in Example 8.
Example 8 – Using an interrupt to shut down a continuous process
.
.
.
RWRunnableSelf self = rwRunnable();
// Perform iterative operation until interrupted...
while(!self.serviceInterrupt()) {
// Perform operation
}
// Operation interrupted!
.
.
.
To shutdown the iterative process, an external thread calls requestInterrupt() and follows that with a call to releaseInterrupt():
.
.
.
// "theThread” is the iterative process
// Request shutdown!
if (RW_THR_ABORTED == theThread.requestInterrupt()) {
// Oops! The process isn’t running anymore!
}
else {
theThread.releaseInterrupt();
}
.
.
Rendezvous Synchronization
You can cause a runnable to interrupt itself by using the interrupt() method in the RWRunnableSelf class. This allows you to implement rendezvous or barrier synchronization. A rendezvous defines a meeting point between two threads. The thread that arrives at the meeting point first must wait for the other thread to arrive before continuing.
Example 9 shows how to use the interrupt() and requestInterrupt() calls to implement a simple rendezvous. The thread on one side of the rendezvous uses interrupt() to block itself while waiting for the other thread to arrive:
Example 9 – Implementing a simple rendezvous between threads
RWRunnableSelf self = rwRunnable();
while(/*condition*/) {
...
// Do something useful...
...
// Rendezvous with the other thread
// Wait here until the other thread releases the interrupt!
self.interrupt();
}
The thread on the other side of the rendezvous uses requestInterrupt() to block while waiting for the other thread to arrive:
// "other” points to the runnable containing the other thread
while(/*condition*/) {
...
// Do something useful...
...
// Rendezvous with the other thread
// Wait here until the other thread interrupts!
if (RW_THR_ACQUIRED == other.requestInterrupt())
// Must release twice because the thread was
// interrupted twice
{ other.releaseInterrupt();
other.releaseInterrupt(); }
}
Interrupting Threads at Startup
The threads created by the threaded runnable classes can also be interrupted at startup by using an RWThreadAttribute instance to specify a start policy of RW_THR_START_INTERRUPTED. The releaseInterrupt() function must be used to release any thread interrupted in this manner.