Actions handled by obey have two phases: phase 0 at invocation and phase 1 at the end of the action. The obey method calls obey_0 for phase 0 and obey_1 for phase 1. The obey_0 method creates an action thread to execute the action; obey_1 recovers (i.e. joins with) the action thread.
The table actions_pt_2, also private to the Camera-facade, specifies which private method of the class should be run in the action thread. This table is a local mechanism of udas_camera and is nothing to do with the DRAMA libraries. The table contains addresses of action methods to be executed.
The tables actions_pt_1 and actions_pt_2 are both indexed by the array action_codes, which is an enumeration of symbolic codes for the actions. Values from action_codes are set into actions_pt_1 such that they can be retrieved by a call to DitsGetcode, and then used as an index into actions_pt_2 to fetch the address of the method needed to execute the action. Clearly, alterations to action_codes, actions_pt_1 and actions_pt_2 need to be coordinated to preserve the indexing.
An action object records the identifier for the thread doing the action, the (DRAMA) name of the action, the index of the action (needed for signaling back to the DRAMA thread) and any Exception arising during the action.
Action objects follow the planned family pattern. There is on Action
for each DRAMA action, and the objects are reused (with appropriate reinitialisation)
in successive invocations of an action.
Figure 4.1.1: life-cycle for an action thread.
The thread is started with a specific set of Pthreads attributes that are common to all action threads. The Camera-facade creates the attribute object at construction and uses it each time a thread is started. If a new set of attributes were used for each thread, the attributes object would be leaked, as libpthread doesn't manage these objects. The attributes imposed system-scope scheduling on the thread such that it is bound to a light-weight process in the kernel and is scheduled by the kernel instead of by libpthread. Kernel scheduling is less efficient, in that context switching takes longer, but scheduling by libpthread is ineffective, in that the DRAMA thread is starved of CPU and reacts too late to some events.
All the action threads are started in the method Camera_facade::do_action. That method then calls the action method named in the Action object and blocks, waiting for the action method to return. After the action method returns, do_action calls DitsSignalByIndex, and returns. That sequence causes the DRAMA thread to reschedule the action, to call obey_1 and hence to join with the action thread.
When an action is kicked, the kick handler kick cancels the action thread. It gets the identifier of the thread from the action object and calls pthread_cancel. The kick handler then returns, causing the obey handler to be scheduled again, which in turn causes the cancelled thread to be joined.
At no point does an action method start a new thread. If a worker thread is needed, it is started by one of the objects that the action method calls. (See particularly the Run class.)
The action-argument structure has a field for every possible argument of all the actions. Most of the fields are unused in most cases.