Process file fan-out

Designer's notes #15 - Home - Prev - Next
Řyvind Teig, Trondheim, Norway (

Functions out there need not destroy black-box encapsulation

Right, you see the structure of an ANSI C process (thread, task) (see [14]) - from start to end. When processes communicate by sending messages (over channels) to each other, and by no other means, it is essential to minimize other files' access to internal data and functions.

Here's the pattern for black-boxing:

1. Only two externaly callable (non static) functions should exist - the P_MyProcess itself, and an I_MyProcess function to initialize the process. The non-preemptive (cooperative) scheduler below it may easily schedule (call)  a non-parameterized process (function) from a queue of function pointers. A process runs to completion every time it has executed the thread (it deschedules itself by running to final } as finished for this round). So the scheduler will call the process proper at the beginning { every round. The scheduler does not need to restore the process' state and continue after the line of code where some preemption may have set it aside. It's much easier. (Observe note 18 that describes a somewhat different scheme.)

2. The process file should not contain any other externally available functions. This would cause leakage and break process encapsulation. The only exception should be operating system break-in debug functions.

3. The malloc'ed Context should contain absolutely all data which need to survive a descheduling. Temporary local variables, as allocated on the stack (like ContextPtr) -  are ok

4. All functions called should be parameterized with values from Context. If the process is small, like a buffer process or a UART driver, it may need to call little or no external functions. (Any external function may be called from any process, there is no reentrancy problems, since processes always run atomically to completion.)
Since the basically synchronized communication between processes is implemented by returning function calls, like Chan_out and Chan_in_ALT, it follows that the associated data being moved also must be entities in each process' Context. When a process blocks for a finished communication event, it does so simply by returning to the rtx after the Chan_.. call.

... This is a fold containing folds, code or comments (see [10])
... #includes
... local typedefs and #defines

typedef struct // contains _all_ variables
    P_State_a    P_State;
    P_SchedCnt_a P_SchedCnt;
    P_ExtFunc1_a Extern1Data; // typedef'ed in .h file
} Context_a;

typedef Context_a* ContextPtr; // malloc this

... functions (all static & internal)

void I_MyProcess (ProcId_a P_This, Chan_a* C100,C201,C202) 
ContextPtr_a ContextPtr;
... Init from params and do malloc 
    g_ThisContextPtr = (void*)ContextPtr; // to rtx

void P_MyProcess (void) 
{ // from rtx - every scheduling to this point
    ContextPtr_a ContextPtr = (ContextPtr_a)g_ThisContextPtr; // from rtx
    switch (ContextPtr->P_State)
        ... ST_STATE_IN_ALT_100_201_A
        ... --> ST_STATE_IN_ALT_100_201_END_A
        ... ST_STATE_IN_100_A
        ... ST_STATE_IN_201_A
        ... ST_STATE_OUT_202_A
   ... Work
} // to rtx - every descheduling
5. It is good practice to move code out of the process file. This could be common files or special "state machines" used by only one process. In that case, the externally called functions need to know their structs, defined in their .h files. These structs reside inside Context and pointers to them are sent over on the calls. Of course, these external  functions should never access anything else than the parameters.
If they do access statics and thus keep their own state (like a process), it's getting more difficult. See [9].
6. Don't place the Context_a struct in a header file. This way it's certain that no programmer would pass over a pointer to all of the process' internal data. This could prove devastating.

4.Oct.07 (initial), 20Oct09

Other publications at