The COUGAAR Thread Services are designed to replace the direct use of Java Threads with a threading system that can be more tightly controlled. The central new interface is Schedulable, which can be thought of as taking the place of a Java native thread. Schedulables are created and returned by the ThreadService and started by client code. Unlike native Java threads, Schedulables don’t necessarily run immediately. The Threading Services allow only a certain number of threads to be running at once. If a Schedulable is started when all threads slots are in use, it will be queued and will only run when enough running threads have stopped to allow it reach the head of the queue. The maximum number of running Schedulables as well as the queue ordering can be controlled by the ThreadControlService. The Thread Services can also be used to schedule Java TimerTasks.
In addition to the running of threads and tasks, the COUGAAR Thread Services offers two other features: an event-like mechanism for receiving callbacks when ‘interesting’ events occur ( ThreadListenerService), and a certain amount of explicit control over scheduling. These interfaces, as well as the Schedulable and ThreadService interfaces, are described in detail below.
The COUGAAR Thread Services are hierarchical. Each Agent has a set of Thread Services of its own, as does the MTS and certain other Node-level components. These local Thread Services handle threading at their own level and are in turn controlled by a root Thread Service at the Node level. The inter-level control mechanism takes the form of “rights” given by the higher to the lower level services, and returned after use by the lower to the higher level services. When the higher level service has multiple children, a RightsSelector is used to choose the child service that will be the next to receive rights (round-robin by default). In principle this hierarchy could be extended to a further depth but so far we haven’t found any good reason to do that in practice.
Within a level, scheduling is further subdivided into lanes. There are currently four hardwired lanes. A lane is associated with a Schedulable at creation time and remains fixed for the lifetime of the Schedulable.
The design of the thread services has important implications for the runtime pattern of the Runnables it will be running. In particular, it’s not generally a good idea for these Runnables to block. The common Java pattern of a loop with a wait or sleep call should usually be unwrapped into a simple ‘strip’ of code that reschedules itself if it needs to run again. Examples can be seen in Use Cases and Examples. Code which needs to block or to use an unusual amount of resources should run in the appropriate lane if at all possible.