Threads Module User's Guide : PART II Concurrency Packages : Chapter 3 The Threading Package : The Runnable Object Classes : Starting a Runnable
Starting a Runnable
Starting a runnable initiates the execution of the runnable’s task, as defined by its run() member. A start operation results in the synchronous or asynchronous execution of a runnable’s defined task, depending on the type of runnable. Asynchronous runnables create new threads to complete their tasks.
You must start a runnable object before it can begin execution of any activity or task that it defines. All runnable objects are started by calling the runnable’s start() function.
 
RWCompletionState
RWRunnable::start(void)
By starting a runnable, you begin a process that results in the eventual execution of that runnable’s virtual run() member. Each runnable class redefines the run() member to supply its own class-specific behavior.
The functor-based runnable classes include a version of the run() function that invokes the functor object supplied by some client. Similarly, the runnable server classes redefine run() to dequeue runnable objects and execute them by calling their start() members.
Although all runnables are started in the same way, the underlying start processes differ. The differences stem from the way a particular runnable class dispatches its run() function. The synchronous runnable classes have a different start-up process compared to that of the asynchronous, or threaded, runnable classes.
In the descriptions that follow, the numbers shown in the figures indicate the transitions produced by various events or operations. Numbers that appear inside square-brackets [x] refer to transitions in the execution state diagram. Numbers that appear in side curly braces {x} refer to transitions in the completion state diagram.
Starting Synchronous Runnables
A synchronous runnable does not create its own thread to accomplish its tasks. It must use, or borrow, the thread that calls start() to execute the run() member. It is designed to pass control from the start() function to the internal run() function after performing any necessary initialization tasks.
Because run() is executed synchronously, start() cannot return control to its caller until run() returns control. This process is explained in the following steps and is illustrated in Figure 9.
1. When first constructed, a runnable object is initialized with an execution state of RW_THR_INITIAL[1] and a completion state of RW_THR_PENDING{1}.
2. The runnable briefly enters an execution state of RW_THR_STARTING[2] before entering its run() function, when the state is changed to RW_THR_RUNNING[3].
3. Once the runnable successfully completes its activities and returns from its run() function, the state reverts back to RW_THR_INITIAL[4] and start() returns a completion state of RW_THR_NORMAL{2}.
4. If an exception is thrown as a result of runnable activities and propagated back out of run(), the runnable enters the RW_THR_EXCEPTION[5] state before returning to the RW_THR_INITIAL[6] state and returning a completion state of RW_THR_FAILED{3}.
Figure 9 – Start operation for synchronous runnables — interaction and timing
You can start a synchronous runnable any number of times, but only one thread at a time can execute the encapsulated function within the runnable. If a second thread attempts to start a runnable that is currently being executed by another thread, the start() function produces an RWTHRThreadActive exception.
Checking the Completion State
You can use the completion state value returned from the start() function to determine whether or not the runnable ran to completion. The start() function returns the following completion states:
RW_THR_NORMAL if the runnable ran to completion.
RW_THR_FAILED if an exception was produced.
RW_THR_CANCELED if the runnable was canceled by another thread.
The code fragment in Example 4 shows how to check for completion states.
Example 4 – Checking a runnable’s completion state
switch(runnable.start()) {
case RW_THR_NORMAL:
// The runnable ran to completion
...
break;
case RW_THR_FAILED:
// The runnable exited with an exception
...
break;
case RW_THR_CANCELED:
// The runnable was canceled during execution
...
break;
}
Starting Threaded Runnables
An asynchronous, threaded runnable creates its own thread to accomplish its tasks. When you call RWRunnable::start(), the runnable launches a new thread to execute the run() member. The process is explained in the following steps and is illustrated in Figure 10.
1. When first constructed, a threaded runnable object is initialized with an execution state of RW_THR_INITIAL[1] and a completion state of RW_THR_PENDING{1}.
2. The runnable enters an execution state of RW_THR_STARTING[2] before creating the new thread. The starting thread requests and waits for the new thread to interrupt.
The interrupt occurs before the new thread enters its run() member. This step insures that the thread doing the starting does not return from the start() function until the new thread completes any necessary initialization. Once the new thread is interrupted, the Threading package sets the initial scheduling policy and priority for the thread.
3. Once the new thread begins execution, it attempts to service the interrupt request. This causes the execution state to change to RW_THR_INTERRUPTED[7].
4. The start policy of the runnable is evaluated. Depending on the current policy setting, the new thread is either left in the interrupted state or released. In either case, the start() function returns control to the calling thread.
a. If the threaded runnable has a start policy value of RW_THR_START_INTERRUPTED, the start() function returns without releasing the interrupt.
b. If the threaded runnable has a start policy of RW_THR_START_RUNNING, the interrupt is released by the starting thread, the execution state is changed to RW_THR_RUNNING[9] state, and the start() function returns a completion state of RW_THR_PENDING to the calling thread. For more information about the start policy and other thread attributes, see “Thread Attributes.”
5. Once a runnable’s thread successfully completes its activities and returns from the run() function, the runnable’s execution state is changed to RW_THR_INITIAL[4] and its completion state is set to RW_THR_NORMAL{2}.
6. If an exception thrown from within the run() member is successfully caught within the runnable, the completion state changes to RW_THR_FAILED{3} and the execution state briefly changes to RW_THR_EXCEPTION[5] before changing back to the RW_THR_INITIAL[6] state.
Figure 10 – Start operation for threaded runnables — interaction and timing
Distinguishing Between Synchronous and Threaded Runnables
The completion state value returned from the start() function can be used to distinguish between a synchronous runnable and a threaded runnable, as shown in Example 5. The start() function on a synchronous runnable always returns a completion state of RW_THR_NORMAL, RW_THR_FAILED, or RW_THR_CANCELED. Threaded runnables always return RW_THR_PENDING.
Example 5 – Using completion state to distinguish synchronous from threaded runnables
void executeRunnable(RWRunnable& runnable)
{
RWCompletionState result;
if (RW_THR_PENDING == (result = runnable.start())) {
// Threaded Runnable
...
}
else {
// Synchronous Runnable
...
}
}
Restarting a Threaded Runnable
You can restart a threaded runnable, but only after the thread created by the previous call to start() has exited. Any attempt to call start() on a threaded runnable that already has an active thread results in an RWTHRThreadActive exception.