We previously saw that every module has to expose at least 5 functions.
We will now go deeper, better understanding a module’s lifecycle.
First of all, module lifecycle is automatically managed by libmodule; moreover, when using Easy API, module registration/deregistration is completely automated and transparent to developer.
This means that when using easy API (or multicontext API), you will only have to declare a source file as a module and define needed functions.
Before any module is registered into libmodule, each module’s module_pre_start() function is called.
This function can be useful to set each module pre-starting state: this may be needed if any check() function depends on some global state (eg: as read by config file).
After every module_pre_start() function is called, libmodule will start checking which module needs to be registered, eventually registering them.
For every module, check() function will be called; if and only if that function returns true, the module will be registered.
An unregistered module is just dead code; none of its functions will ever be called.
Once a module is registered, it will be initially set to an IDLE state. Idle means that the module is not started yet, thus it cannot receive any PubSub msg nor any event from fds.
Right after module’s registration, its evaluate() function will be called, trying to start the module right after it was registered.
Evaluate will be then called at each state machine change, for each idle module.
As soon as module’s evaluate() returns true, the module is started. It means its init() function is finally called and its state is set to RUNNING.
A single module can poll on multiple fds: just call module_register_fd() multiple times.
When a module reaches RUNNING state, modules_loop()/modules_ctx_loop() functions will finally receive events from its fds.
Whenever an event triggers on a module’s fd, or the module receives a PubSub message from another one, its receive() callback is called.
Receive callback will receive userdata too as parameter, as set by module_set_userdata().
Finally, when leaving program, each module’s destroy() function is automatically called during the process of automatic module’s deregistration.
When dealing with libmodule’s Complex API, no modules is automatically started for you, ie: you must manually call module_register()/module_deregister() on each module.
When using complex API, you are responsible to register/deregister modules, and thus initing/destroying them.
Note that with Complex API, module_pre_start() function is not available (it would be useless), and you won’t need to define check() function.
You will still have to define evaluate(), init(), receive() and destroy() functions (but you can freely name them!).
Everything else but module’s (de)registration is same as Easy API.
As previously mentioned, a registered module, before being started, is in IDLE state.
IDLE state means that it has still no source of events; it won’t receive any PubSub message and even if it registers any fd, they won’t be polled.
When module is started, thus reaching RUNNING state, all its registered fds will start being polled; moreover, it can finally receive PubSub messages. Fds registered while in RUNNING state, are automatically polled.
If a module is PAUSED, it will stop polling on its fds and PubSub messages, but PubSub messages will still be queued on its write end of pipe. Thus, as soon as module is resumed, all PubSub messages received during PAUSED state will trigger receive() callback.
If a module gets STOPPED, it will stop polling on its fds and PubSub messages, and every autoclose fd will be closed. Moreover, all its registered fds are freed. STOPPED state is the same as IDLE state, but it means that module was started at least once.
module_start() needs to be called on a IDLE or STOPPED module.
module_pause() needs to be called on a RUNNING module.
module_resume() needs to be called on a PAUSED module.
module_stop() needs to be called on a RUNNING or PAUSED module.