The thread programming interface for uCR was revised several times before the current interface was settled on. At first, we designed threads as objects with interesting methods and put them in ThreadQueue containers. Eventually, however, we chose to attach most of the thread methods to the ThreadQueue class and left the THREAD a passive, opaque object. The run queue, we reasoned, would be a ThreadQueue that the programmer can manipulate like any other ThreadQueue. Threads in the run queue would be subject to execution, and could be suspended simply by pulling the thread from the run queue and placing it elsewhere.
class ThreadQueue { public: // Pass true to the flag to // put the thread in front. void enqueue(THREAD*, bool=false); THREAD*pull(); THREAD*peek(); };
This proved successful, though occasionally cumbersome and less clear then the more conventional POSIX-style thread functions. uCR internally uses the ThreadQueue class for the run queue and suspension lists in synchronization primitives. These instances are generally hidden from the application programmers, who have ultimately chosen to use POSIX-style thread functions provided in the uCR library. Programmers may use the ThreadQueue class to implement new synchronization primitives, if desired.
The threads themselves are opaque objects of type THREAD, and are passed around to the ThreadQueue objects and thread manipulation functions like a thread identifier in a more conventional thread package. The THREAD object is a completely opaque token used by the programmer, and uCR, to represent the object that is a thread. This is more an abstract data type design then an object oriented design. It is a semantic quirk that this C abstract data type is much like a concrete object class in C++.
The idea of a thread as an abstract class with a virtual method for its behavior is known to us. Some call this paradigm an active object. [2] Active objects are different from passive objects in that they have their own thread of execution and activate passive objects by calling methods. Threads and interrupt handlers are two different kinds of active objects, synchronization primitives and devices examples of passive objects.
We experimented with active objects, but ultimately decided to implement threads using the THREAD token and a set of conventional thread manipulation functions. The traditional thread functions are well established, easy to use, small and generally don't come into play once the threads are created and started. However, the specific interface built-in to uCR allows for efficient implementation of active objects if desired.