Lib/sequencer offers Multitasking
The lib/sequencer library implements an event-driven, priority-based, cooperative multitasker. The sequencer library does not start new threads or any other separate processes. Unlike "pure" cooperative multitasking, a task cannot stop at any time (using a hypothetical yield feature) but only in known and stable states. The sequencer library does it this way to retain conformance to the Eiffel principles.
The principle is:
- the class LOOP_STACK manages the multitasking;
- the class JOB represents a task. The task core is the continue feature, during the execution of which the task controls the whole system;
- the class EVENTS_SET describes the condition(s) in which a task can be executed.
We will successively document each of those concepts:
The multitasking manager
The class LOOP_STACK is in charge of managing the multitasking. It is used in two steps:
- initialization: creation of the LOOP_STACK object and adding the task(s) to be executed
- execution: execution of the run feature
Of course, additional tasks can be added during the execution. The manager can also be stopped, thanks to the break feature.
A task has a life cycle represented by the features executed by the manager (indeed a LOOP_ITEM hence the export clauses of those features).
- prepare allows to prepare the task; in this phase, the task sets the events upon which it wants to be activated. The EVENTS_SET object is an object upon which one can set conditions (thanks to its expect feature).
- is_ready allows to test if the task has really been activated.The EVENTS_SET object is an object upon which one can test conditions (thanks to its event_occurred feature).
- continue contains the execution body of the task, and is executed if the task is really activated.
- done tells if the task has finished its execution. If so, it will be removed from the execution loop. Otherwise, the cycle begins again.
- restart allows to reinsert a task in the execution loop.
The restart feature is seldom useful. See Vision for some use cases.
The possible conditions are:
- Time: this allows to create periodic tasks. See the classes PERIODIC_JOB, BACKGROUND_JOB, and TIME_EVENTS;
- Network: this allows to build tasks waiting network connections (feature SOCKET_SERVER.event_connection).
The manager implements the following cycle:
- call to the feature EVENTS_SET.reset;
- call to the feature JOB.prepare on each task;
- call to the feature EVENTS_SET.wait to wait for at least one condition to happen;
- call to the feature JOB.is_ready on each task;
- for all the ready tasks, call to the feature JOB.continue;
- for those tasks, call to the feature JOB.done and if relevant, remove the task;
- and back to the beginning.
Tasks are executed by priority order. Only the ready task with the lowest priority is executed. The others, even if ready, will have to wait a future time when no other task with a lower priority is ready.
-note: we need to document how many priority levels
are supported and how the scheduling works when multiple or all tasks were given
an identical priority level. Would the scheduler then resort to some sort
of round-robin method? By what rules? Perhaps by evaluating the duration of the wait for next execution? /HZ
Libraries using the sequencer
- lib/net: servers usually allow to accept many simultaneous connections. This is implemented with the sequencer library.
- lib/vision: the event manager loop is implemented with a sequencer.