Overview

The basic, linear, Life Cycle was described previously. Here, we consider the issues that emerge when we engage more advanced modes of operation. In particular, we consider the sub-phases of the Life Cycle in detail, and see what their significance is. The basic Life Cycle is repeated below, for convenience. We have filled in the Summary column with the actions that a more comprehensive Process might take.

EventMultipleSummary
Init Phase
EVENT_STATE_SETUnserialize State to memory from StateML.
EVENT_INIT_PRECONNECTValidate input count, presence on Sets.
EVENT_INIT_CONNECTYesReview/validate offered inputs; Create outputs.
EVENT_INIT_POSTCONNECTPre-calculate Temporal Parameters (timing data is now valid).
EVENT_LOG_INITPrepare resources for logging data during Run Phase.
Run Phase
EVENT_RUN_PLAYAcquire external resources.
EVENT_RUN_RESUMEAcquire real-time external resources.
EVENT_RUN_SERVICEYesService input/output Ports (incidentally, do computation).
EVENT_RUN_PAUSERelease real-time external resources.
EVENT_RUN_STOPRelease external resources.
Term Phase
EVENT_STATE_GETSerialize State from memory to StateML.
EVENT_LOG_TERMReturn Run Phase logs to Framework.

Run State

In Run Phase, we describe the Process as being in one of the following Run States: STOP, PAUSE or PLAY. Four of the five Run Phase events move the process between these states, as shown in the table below. Movement between these states is key to supporting advanced modes of execution (sub-sections, below).

EventMultipleSummary
Run Phase
EVENT_RUN_PLAYRun State from STOP to PAUSE
EVENT_RUN_RESUMERun State from PAUSE to PLAY
EVENT_RUN_SERVICEYes
EVENT_RUN_PAUSERun State from PLAY to PAUSE
EVENT_RUN_STOPRun State from PAUSE to STOP

More advanced processes may need to take action during these transitions, and how to choose these actions is the subject of this section. Loosely, a Process in each of these states should have less or more of its external interactions (interactions with things other than the Framework, e.g. the OS or any attached hardware) in a ready state, as shown in the table below.

Run StateReady
STOPNo external interactions ready.
PAUSEAll, except real-time ready.
PLAYAll external interactions ready.

In STOP, therefore, a Process should consist of just an in-memory representation of its StateML, along with internal (Framework) interactions such as handles to input and output Ports.

Pause-Resume Execution

The user may wish to pause processing during an execution, e.g. to check or record the state of some part of the system, or something attached to it, or simply to gaze in wonder at the beautiful data in their GUI. When the Execution is paused, its superficial state (everything the Framework is aware of) does not need to change. However, some Processes that have external interactions may need to pause those interactions whilst the System is paused. In particular, any Process that has a real-time interaction (particularly with a physical system) will want to suspend that interaction cleanly whilst the System is paused. Note that the System may be paused for any length of Wall Clock time. The possibility of Pause and Resume introduces the following modification to the Life Cycle.

EventMultipleSummary
Run Phase
EVENT_RUN_PLAYRun State from STOP to PAUSE
Resume-Pause Cycle
EVENT_RUN_RESUMERun State from PAUSE to PLAY
EVENT_RUN_SERVICEYes
EVENT_RUN_PAUSERun State from PLAY to PAUSE
Resume-Pause Cycle
EVENT_RUN_RESUMERun State from PAUSE to PLAY
EVENT_RUN_SERVICEYes
EVENT_RUN_PAUSERun State from PLAY to PAUSE
...
EVENT_RUN_STOPRun State from PAUSE to STOP

This Resume-Pause Cycle (Resume, Service, ..., Service, Pause) may occur multiple times (including zero) within Run Phase, and the System may spend arbitrary lengths of time in the Run State PAUSE. The Process should pause real-time interactions cleanly as it is moved into PAUSE and resume (or start) them cleanly as it is moved into PLAY. However, it should do nothing else - these transitions are intended to support real-time interactions only. If other processing is done, here, this will impact on the time taken for the whole System to move between PAUSE and RUN states; this may annoy the user, but more seriously may adversely affect the operation of Peer Processes that rely on a fairly rapid transition.

To be clear, a Process that does not have external real-time interactions (the vast majority of Processes) can safely ignore these two transition events.

Stop-Play Execution and Dynamic Load Balancing

The user may wish to stop processing during an execution, e.g. to store the System to disk for later revival. In this case, all Processes will be asked to serialize (EVENT_STATE_GET) and unserialize on revival (EVENT_STATE_SET) - this can be seen as being a bit like hibernation. Since any external interactions cannot be persisted over this interval, all resources (strictly, as mentioned, all external interactions) must be discarded and reestablished after revival. The STOP state does not mean that the System has been serialized, just that it is ready to do so.

When a System is revived (restarted by the user), note that it may be restarted on a different hardware system (even, potentially, on a different platform). This underlines why OS resources, as well as other external interactions, must be released in the meantime. In addition, if a System is undergoing Dynamic Load Balancing, individual Processes may be moved into STOP, asked to serialize, sent to another Voice, asked to unserialize, and moved back to PAUSE. For these reasons, it is clear that all external resources must be released when moving into STOP.

Note that the most common reason for moving into STOP is that the System has finished execution. Whilst this is the most common, it does not define what you should do - assume you are moving into STOP for one of the above reasons, and you will be doing the right thing anyway.

Whilst in PAUSE, the System can be described as "ready to run", in STOP it can be described as "ready to get ready to run". That is, it is assumed that moving between STOP and PLAY may take an arbitrary amount of Wall Clock time, and a Process need not feel embarassed about this. Conversely, the state STOP can be described as "ready to serialize", whilst PAUSE can be described as "ready to get ready to serialize". Where a Process in PAUSE should have all of its non real-time external interactions ready, a Process in STOP should have them all terminated, ready to serialize and terminate.

EventMultipleSummary
Run Phase
EVENT_RUN_PLAYRun State from STOP to PAUSE
Resume-Pause Cycle
EVENT_RUN_RESUMERun State from PAUSE to PLAY
EVENT_RUN_SERVICEYes
EVENT_RUN_PAUSERun State from PLAY to PAUSE
EVENT_RUN_STOPRun State from PAUSE to STOP
EVENT_STATE_GETSerialize
...Data transfer or hibernation.
EVENT_STATE_SETUnserialize
...Reestablish Peer connections (not yet implemented)
Run Phase
EVENT_RUN_PLAYRun State from STOP to PAUSE
Resume-Pause Cycle
EVENT_RUN_RESUMERun State from PAUSE to PLAY
EVENT_RUN_SERVICEYes
EVENT_RUN_PAUSERun State from PLAY to PAUSE
EVENT_RUN_STOPRun State from PAUSE to STOP

We can see that only Processes that support EVENT_STATE_GET can actually undergo Stop-Play execution or Dynamic Load Balancing. Implementing EVENT_STATE_GET is unnecessary if you do not want to support these modes of operation; conversely, not implementing it will mean your Process cannot support these modes, at least not without further development work.

Process and Engine Interactions

Here, we show Process and Engine proceeding through the Life Cycle in parallel. This section tells you less about the Process, and will be of more use to anybody wishing to understand the operation of the Engine. Note that we show only the everyday Life Cycle, without the nuances introduced above for more complex modes of operation.

Process Life Cycle

This is the Life Cycle of an Execution from the point of view of a Process, which runs in a Worker Thread. This Worker Thread may change under some conditions - see Thread Sensitivity.

In brief, a BRAHMS process must do three things, as follows.

Init
Read its parameters (State) into memory, review structure of its inputs, generate structure of its outputs.
Run
Respond to requests to service its input and output interfaces at a given time instant (EVENT_RUN_SERVICE). This implies that the process must complete any processing to bring itself up to date with the specified time.
Term
Return results to the Engine and free any resources it used.

Engine Life Cycle

This is the Life Cycle of an Execution from the point of view of the Engine, which runs in the Caller Thread.

Pre-amble

  • Instantiate globals.
  • Interpret command-line arguments.
  • Instantiate workers (Alice, Bob and Charlie).
  • Call init(), run() then term() on Bob (detailed below).
 

Init Phase

Notes
  • There is considerable flexibility as to exactly when some particular bit of initialisation or termination is done. A state that needs to be zeroed before Run Phase, for instance, can be zeroed anywhere from the constructor to EVENT_RUN_RESUME. Some choices are up to the developer - you might choose whatever generates the most readable code, for instance - but there will sometimes be performance issues. Some discussion on this, below, is indicated with INIT or TERM.

CREATE

PRECONNECT

CONNECT

Until all inputs have been seen:

  • Receive EVENT_INIT_CONNECT
    • On the first call, zero or more inputs are available.
    • On each call, validate (type, structure) any available inputs, create as many outputs as possible.
    • On each call after the first (if any), more inputs are available than on the previous call.
    • On the last call, all inputs are available (note that this may be the first call as well).

POSTCONNECT

  • Receive EVENT_INIT_POSTCONNECT.
    • Update state (e.g. time-sensitive constants) if necessary (base sample rate now available, and input structures entirely known)

START LOGS

Init Phase

READ XML

SOCIALIZE

  • (Concerto only) Connect channels to all other voices.

CREATE

For each Process:

PRECONNECT

For each Process:

  • Set up structure of input interface, so Process knows how many inputs (and in what sets) it will receive (though the inputs themselves are not there yet).
  • Advise Process (EVENT_INIT_PRECONNECT).

CONNECT

If at any run through the following loop, no progress occurs, E_DEADLOCK is raised.

Until all Processes have seen all inputs:

POSTCONNECT

START LOGS

For each Process:

 

Run Phase

  • Receive EVENT_RUN_PLAY
    • INIT You should do most of your heavy-duty initialisation here, since before this, there are a number of reasons for the system to go down prematurely, and there's little point in making your users wait for slow initialisation if the system isn't going to get past the CONNECT phase, say. In addition, future implementations may load the Process into a different context before entering run phase, to give them as much resources as possible, so heavy-duty work may proceed faster here than in previous events. You should initialise anything that is Context Sensitive, here.
Notes
  • This resume-service-...-service-pause cycle may execute more than once.
  • Receive EVENT_RUN_RESUME
    • INIT Perform initialisation that must occur as late as possible (Wall Clock time) before the process starts run-phase processing proper, e.g. start hardware clocks. This time-slice is shared, so the process should do as little processing as possible, performing initialisation in an earlier phase if possible. Essentially, only real-time interactions should be initialised here.

Until stopped (Soft Stop or Hard Stop):

  • Receive EVENT_RUN_SERVICE
    • Read any inputs that are Due.
    • Perform any processing required (as a pre-requisite to writing outputs).
    • Write any outputs that are Due.
    • Request execution cancel if appropriate.
  • Receive EVENT_RUN_PAUSE
    • TERM Perform termination that must occur as early as possible (Wall Clock time) after the process ends run-phase processing proper, e.g. stop hardware clocks. This time-slice is shared, so the process should do as little processing as possible, performing operations in a later phase if possible. Essentially, only real-time interactions should be terminated here.

Run Phase

CALLER THREAD

Until all worker threads have terminated:

  • Service GUI.
  • Check worker threads for hangs and errors.
  • Sleep a while.

WORKER THREAD(S)

Until Soft Stop (usually, reached Execution Stop Time) or Hard Stop:

 

Term Phase

STATE

LOGS

Post-amble

  • Component deleted (EVENT_MODULE_DESTROY, destructor executed).
    • TERM Critical termination (shutting down hardware, for example) should be performed in the destructor (possibly in addition to an earlier event), since in the case of critical failures, the destructor is the least likely event to not get called.

Term Phase

STATE

For each Process:

LOGS

For each output of each Process:

For each Process:

REPORT

  • Collate performance data from all Components.
  • Create Report File.

Post-amble

  • Delete all Components.
  • Unload Component Modules.
  • Delete Globals Object.
  • Report any errors.
  • Exit.