3.6 The Server Classes
As explained in Section 3.5.1.2, “Runnable Servers,” a runnable server is a threaded runnable that accepts and queues any number of runnable objects and executes them within its own internal thread or threads.
The runnable server classes include:
*RWRunnableServer —A threaded runnable that accepts runnable objects and executes them using its own, internal thread. An application using an RWRunnableServer will contain at least two threads: the main thread that created the RWRunnableServer, and the thread that is running the RWRunnableServer do-loop.
The RWRunnableServer do-loop performs the following operations until a termination condition (cancellation or stop) occurs:
*Get the next available runnable (may block).
*Check for a termination condition.
*Execute the runnable.
*RWServerPool — A threaded runnable that accepts runnable objects and dispatches each one to the next available RWRunnableServer object from an internal pool of such objects. An application containing an RWServerPool will contain at least 2 + n threads: the main thread that created the RWServerPool, the thread that is running the RWServerPool do-loop, and n RWRunnableServer threads each executing the do-loop of an RWRunnableServer in the RWServerPool's internal queue.
The RWServerPool do-loop performs the following operations until a termination condition (cancellation or stop) occurs:
*Get the next available runnable (may block).
*Check for a termination condition.
*Get the next available runnable server (may block).
*Check for a termination condition.
*Enqueue the runnable on the runnable server.
3.6.1 Constructing a Server
The only way to construct a runnable server is to use one of the static make() functions in the two server handle classes, as shown in Example 20.
Example 20 – Constructing a runnable server
RWRunnableServer myServer;
myServer = RWRunnableServer::make();
 
RWServerPool pool;
size_t poolSize=4;
pool = RWServerPool::make(poolSize);
Each of these classes also have additional static make() functions that accept other parameters:
*Some forms of make() accept RWThreadAttribute instances that can be used to establish the initial attributes of any threads these classes create. See Section 3.9.11.1, “Supplying RWThreadAttribute Instances To Threaded Runnables,” for more information.
*Other forms of make() accept a size_t value that specifies the maximum capacity of the server’s input queue, effectively limiting the number of unprocessed runnables that are allowed to accumulate within the queue. If the capacity is left unspecified or is specified as zero, the input queue is treated as if it has unlimited capacity.
3.6.2 Starting a Server
The start() member starts a runnable server instance. When a server pool is started, a sufficient number of runnable servers are started and inserted into the pool before waiting for the first runnable to be enqueued.
3.6.3 Enqueuing Runnables on a Server
You use the enqueue() member to enqueue a runnable for execution. Runnables can only be enqueued between calls to start() and stop(). Any attempt to enqueue a runnable before start() and after stop() produces an exception.
This behavior is different from previous versions of the Threading package. Runnables that were enqueued while the server was not running were simply flushed and ignored prior to version 1.2.
The Threading package allows you to specify a guard functor and a priority value for each runnable instance that you pass to a runnable server. Guard functors are defined in Section 3.5.1.3, “Guard Functors.”
The priority value is used to establish the order in which unprocessed runnables should be considered for execution by the server. Runnables with higher priority values are evaluated and retrieved for execution before runnables of lower priority.
The capacity of a server’s input queue can be limited both at construction and by calling the setCapacity() member. If the capacity of the input queue is limited and the queue becomes full, then the enqueue operations block the caller until the server is able to process a sufficient number of runnables or until the capacity is increased. Each of the basic enqueue() functions is overloaded to give a form that includes a timeout argument. The timeout argument specifies the maximum amount of time to allow for the enqueue operation to complete.
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.
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.
3.6.4 Stopping a Server
The stop() member shuts down a server. This function does not wait for the server to shut down, it merely starts the process. Any thread that needs to know when a server has completed shutdown should use join() to wait for the server thread to exit.
Calling stop() to shut down a server pool causes the server pool thread to attempt to stop and join each of the runnable servers within the pool. Each runnable server only stops after completing execution of any enqueued runnables. Should a runnable server within the pool be deadlocked or running in an infinite loop, then the server pool never completes its shutdown, but still might be canceled by another thread.
A server does not join with the runnables it starts, so threaded runnables passed to a server can continue to execute after the server has been shut down. It is your responsibility to join with any threaded runnables you pass to a runnable server.
3.6.5 Interrupting a Server
An RWRunnableServer services an interrupt request immediately after dequeuing a runnable but before executing it.
An RWServerPool can service an interrupt request at two points:
*After dequeuing a runnable and before dequeuing the next available runnable server.
*Immediately after dequeuing a server, but before enqueuing the runnable for execution.
Once the interrupt is released, both types of server continue normal execution.
3.6.6 Canceling a Server
Canceling an RWRunnableServer instance causes the server to attempt to cancel any current, non-threaded runnable being executed.
Canceling an RWServerPool instance causes the server pool to attempt to cancel all runnable server instances within its pool. Each of these runnable servers in turn attempts to cancel the current runnable, if any, that they are currently executing.
3.6.7 Using Server Pool Thread Attributes
Each runnable server thread in a server pool can be initialized using a thread attributes instance. All threads created following server pool startup have the same attributes because the pool attributes are copied when the server pool is started. Changes to the pool thread attributes do not have any effect until the next time the server pool is started. New runnable server threads added as the result of a resize operation use the thread attribute values as defined when the server pool was started, ignoring any recent changes.
3.6.8 Resizing a Server Pool
Use the resize() member to change the number of runnable servers maintained with a pool. This function does not wait for the server to contract the pool, it merely starts the process.
3.6.8.1 Contracting a Server Pool
Calling resize() to contract a server pool causes the server pool thread to attempt to stop and join with enough runnable servers to shrink the pool down to the new size. Each runnable server being stopped only does so after completing execution of any enqueued runnables. Should a sufficient number of runnable servers within the pool be deadlocked or running in an infinite loop, then the server pool can never complete its contraction of the pool, and so will no longer process enqueued runnables. A server pool in this condition can still be canceled.
3.6.8.2 Creating or Expanding a Server Pool
If the server pool experiences failure while attempting to create or start a new runnable server for the pool, then the server pool attempts to shut down all threads in the pool and exits with an exception. Since the pool expansion is performed by the server pool thread, this type of failure can occur at any time, asynchronous to any call to start() or resize(). To detect this kind of failure, periodically test the completion state of the server pool or register a callback for the RW_THR_EXCEPTION state.