Threads Module User's Guide : PART II Concurrency Packages : Chapter 3 The Threading Package : The Runnable Object Classes : Monitoring the Execution State of a Runnable
Monitoring the Execution State of a Runnable
Each runnable can exist in any one of a number of execution states. The current execution state of a runnable can be queried at any time.
Runnables allow other threads to wait for the runnable to enter a particular execution state. Callback methods can be registered for execution upon entry into states of interest.
This operation is implemented by the following functions:
RWExecutionState RWRunnableHandle::getExecutionState(void) const
RWExecutionState RWRunnable::getExecutionState(void) const
RWExecutionState RWRunnable::wait(unsigned long stateMask)
RWExecutionState RWRunnable::wait(unsigned long stateMask RWExecutionState* state, unsigned long milliseconds)
void RWRunnableHandle::addCallback(const RWTFunctor<void(const RWRunnable&, RWExecutionState)>& functor, unsigned long stateMask, RWCallbackScope scope)
void RWRunnableHandle::removeCallback(const RWTFunctor<void(const RWRunnable&, RWExecutionState)>& functor)
Getting the Instantaneous Execution State of a Runnable
To determine the instantaneous execution state of a runnable, use the getExecutionState() function in the RWRunnableHandle class. This function returns one of the values defined by the RWExecutionState enumerated type, which are listed in “Runnable State.”
You should not use the getExecutionState() function to poll for runnable state changes. A polling loop using this function is unreliable because the runnable could conceivably make several state transitions between any two calls to this function. Also, the execution state retrieved might not reflect the current state of the runnable because the runnable might have shifted to another state by the time you can act on the value returned.
Monitoring Changes in Execution State
The Threading package has two mechanisms for monitoring execution state changes:
A pair of runnable wait functions that you can use to make the calling thread wait until the runnable enters an execution state of interest.
A callback mechanism that allows you to register one or more functors as callbacks to be invoked when the corresponding runnable enters a targeted execution state.
If you must reliably detect runnable state changes in your application, then use one of these two mechanisms. They are described in the following sections.
Using Wait Functions
The RWRunnable::wait() functions are used to wait for a runnable to enter any one of a number of execution states. The Threading package has two types of wait() functions:
One waits an indefinite amount of time for the runnable to enter a targeted execution state.
One accepts a time-limit value expressed as an unsigned long number of milliseconds.
Both functions take a mask value that specifies one or more execution states that trigger an end to the wait. The execution states of interest should be ORed together to form the mask value.
Because a wait can be satisfied by an entry into any one of several states specified by the mask value, each wait() function also returns the execution state value that triggered the end of the wait.
Using Server Classes. The wait functions are useful for detecting state changes in a runnable that has been passed to another thread for execution. The runnable server classes (discussed in “The Server Classes”) accept runnable instances for execution within the server’s thread or threads.
Example. The application code that creates a runnable and passes it to a server class instance might need to know when the runnable began executing. This is accomplished by using one of the wait functions, as shown in Example 14.
Example 14 – Using a wait function to detect state changes in a runnable
#include <rw/thread/rwRunnable.h>
#include <rw/thread/RWRunnableFunction.h>
#include <rw/thread/RWRunnableSelf.h>
#include <rw/thread/RWServerPool.h>
#include <iostream>
 
using namespace std;
 
void hello() {
RWRunnableSelf self = rwRunnable();
do {
cout << "Hello World!” << endl;
} while(!self.serviceInterrupt());
}
 
void main() {
 
// Create and start a server pool instance
RWServerPool server = RWServerPool::make(3);
server.start();
 
// Create and enqueue a runnable that says hello!
RWRunnable runnable = RWRunnableFunction::make(hello);
server.enqueue(runnable);
 
// Wait for the runnable to start executing
runnable.wait(RW_THR_RUNNING);
 
// OK, that’s enough of that!
if (RW_THR_ACQUIRED == runnable.requestInterrupt())
runnable.releaseInterrupt();
 
// Shutdown the server pool
server.stop();
server.join();
}
Using Callbacks
Execution state callbacks are an asynchronous mechanism for reacting to execution state changes. Each time a runnable changes its execution state, it checks for and invokes any callbacks that have been registered for that particular execution state. Any callbacks associated with an execution state are invoked prior to releasing any threads waiting on that same execution state via the RWRunnable::wait() functions.
Callbacks are specified using functors. The functors used for callbacks are based on a different functor class family than those specified for execution within a runnable. These functors are passed the runnable and the current execution state when invoked. This allows the callback function to determine the source and state of each callback invocation. Any function that you create for use as an execution state callback can accept additional arguments and must accept the two values shown in Example 15 as its first two arguments.
Example 15 – Creating a function for an execution state callback
void myCallback(const RWRunnable& runnable,
RWExecutionState state)
{
// Do something useful
}
To use your function as a callback, construct a compatible functor instance that calls the function when invoked, as shown in Example 16.
Example 16 – Using a function as a callback
RWTFunctor<void(const RWRunnable&,RWExecutionState)> myFunctor;
myFunctor = myCallback;
For more information on using and constructing functors, see the Functor page accessible from the Modules tab of the SourcePro API Reference Guide.
Registering the Callback
To register a callback, use the RWRunnableHandle::addCallback() function. This function accepts:
An RWTFunctor instance.
An execution state mask value.
An RWCallbackScope enumerated value that tells the runnable whether the callback should be invoked just once and deleted (RW_CALL_ONCE), or invoked as many times as the execute state requires to satisfy the mask (RW_CALL_REPEATEDLY).
Example 17 – Registering a callback
RWRunnable runnable = ...;
// Register callback that will be invoked each time the
// the runnable is started or completes execution...
runnable.addCallback(myFunctor,
RW_THR_INITIAL | RW_THR_RUNNING,
RW_CALL_REPEATEDLY);
As with the RWRunnable::wait() function, the execution state mask value can specify one or more execution states that trigger the callback. The execution states are ORed together to form the final mask value.
Changing the Execution State
The callbacks for most state changes are invoked and executed by the thread running inside a runnable, but some state change callbacks are invoked by threads that are manipulating the runnable to bring about a change in execution state.
The Threading package does not specify which thread executes a callback because this can change in future versions of the library. You have to explicitly test to see if the calling thread is associated with the source runnable using the threadId() and rwThreadId() functions, as shown in Example 18.
Example 18 – Testing a calling thread’s identity
void myCallback(const RWRunnable& runnable,
RWExecutionState state)
{
// Are we executing in the runnable’s active thread?
if (runnable.threadId() == rwThreadId())
// This is the runnable’s thread!
else
// This is some other thread!
}
You should take care to avoid making changes in the execution state of a runnable from within a callback function. The Threading package does not prohibit attempts to do so, but the results of such an operation can produce undesirable behavior or result in deadlock.
Reusing Functors
The same functor can be registered as a callback on any number of runnables. The runnable handle passed as an argument at invocation can be used to identify the source of the callback. The same functor can also be registered more than once on a single runnable, but you should not have a reason for doing this.
Removing a Callback
The RWRunnableHandle::removeCallback() function is used to remove a callback from a runnable. To remove a callback, you must pass a handle to the same functor that was used to add the callback in the first place. The function removeCallback() removes all callbacks that were registered using the specified functor.