Skip to content

Commit

Permalink
Added some docs + fixes a wrong behaviour: actually call init() callb…
Browse files Browse the repository at this point in the history
…ack even when a module in IDLE state is explicitly started (ie: without a modules_ctx_loop()).
  • Loading branch information
FedeDP committed Jul 5, 2019
1 parent 2a7d11b commit 3125966
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 27 deletions.
13 changes: 10 additions & 3 deletions Lib/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ static int manage_fds(module *mod, m_context *c, const int flag, const bool stop
static module_ret_code start(module *mod, const char *err_str) {
GET_CTX_PRIV((&mod->self));


const bool was_idle = _module_is(mod, IDLE);

/*
* Starting module for the first time
* or after it was stopped.
Expand All @@ -181,6 +184,12 @@ static module_ret_code start(module *mod, const char *err_str) {
MOD_ASSERT(!ret, err_str, MOD_ERR);

mod->state = RUNNING;

/* If this is first time module is started, call its init() callback */
if (was_idle) {
mod->hook.init();
}

tell_system_pubsub_msg(c, MODULE_STARTED, &mod->self, NULL);
return MOD_OK;
}
Expand Down Expand Up @@ -233,9 +242,7 @@ bool _module_is(const module *mod, const enum module_states st) {
map_ret_code evaluate_module(void *data, const char *key, void *value) {
module *mod = (module *)value;
if (_module_is(mod, IDLE) && mod->hook.evaluate()) {
if (start(mod, "Failed to start module.") == MOD_OK) {
mod->hook.init();
}
start(mod, "Failed to start module.");
}
return MAP_OK;
}
Expand Down
2 changes: 1 addition & 1 deletion Lib/public/module/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ _public_ module_ret_code module_stop(const self_t *self);
/* Module generic functions */
_public_ __attribute__((format (printf, 2, 3))) module_ret_code module_log(const self_t *self, const char *fmt, ...);
_public_ module_ret_code module_dump(const self_t *self);
_public_ module_ret_code module_set_userdata(const self_t *self, const void *userdata);

/* Module fds functions */
_public_ module_ret_code module_set_userdata(const self_t *self, const void *userdata);
_public_ module_ret_code module_register_fd(const self_t *self, const int fd, const bool autoclose, const void *userptr);
_public_ module_ret_code module_deregister_fd(const self_t *self, const int fd);

Expand Down
7 changes: 0 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@ Unsurprisingly, module is the core concept of libmodule architecture.
A module is an Actor that can listen on socket events too.
Frankly speaking, it is denoted by a MODULE() macro plus a bunch of mandatory callbacks, eg:
```
cat pippo.c
#include <module/module_easy.h>
#include <module/modules_easy.h>
#include <string.h>
#include <ctype.h>
MODULE("Pippo");
static void init(void) {
Expand Down
12 changes: 3 additions & 9 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,19 @@
- [x] Re-evaluate module_register/deregister parameters (ie: self_t should not be a const!)
- [x] Provide a default (weak symbol) main() that just runs modules_ctx_loop() on any ctx it found
- [x] Rename MODULE_DEFAULT_CTX and MODULE_MODULE_MAX_EVENTS to MODULES_*
- [ ] Hide "const self_t *_self" variable in a function

### Map API
- [ ] Switch to https://github.com/DavidLeeds/hashmap; it is far better

### Stack API
- [ ] Support same "stack_iterator" as per new map API
- [x] Actually call init() callback first time module is started, even without passing from evaluate_module (thus without looping ctx)

### Doc
- [x] module_dump
- [x] module_subscribe/unsubscribe
- [x] self()
- [x] module_register/deregister
- [ ] module_load/unload
- [x] module_load/unload
- [x] module_tell/publish/broadcast
- [x] MODULE_STARTED/MODULE_STOPPED new sysmessages
- [x] Avoid telling system messages like MODULE_STARTED/TOPIC_REGISTERED to ourselves
- [x] Document main() weak symbol!
- [ ] Add a new page about trusting pointers
- [x] Add a new page about trusting pointers

### Samples
- [x] Fix samples
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Welcome to libmodule's documentation!
src/data_structures
src/callbacks
src/lifecycle
src/memory
src/pubsub
src/module
src/modules
Expand Down
9 changes: 3 additions & 6 deletions docs/src/callbacks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,12 @@ Moreover, a module_pre_start function is declared too, but it is not needed by l

.. c:function:: init(void)
Initializes module state; useful to call module_register_fd() for each fd this module should listen to. |br|
To create a non-pollable module, just avoid registering any fd. |br|
Non-pollable module acts much more similar to an actor, ie: they can only receive and send PubSub messages. |br|
Moreover this is the right place to eventually register any topic.
Initializes module state; useful to call module_register_fd() for each fd this module should listen to or to register any topic. |br|
Note that init() is only called first time module is started.

.. c:function:: check(void)
Startup filter to check whether this module should be registered and managed by libmodule, |br|
as sometimes you may wish that not your modules are automatically started.
Startup filter to check whether this module should be registered and managed by libmodule. |br|

:returns: true if the module should be registered, false otherwise.

Expand Down
3 changes: 2 additions & 1 deletion docs/src/lifecycle.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ As previously mentioned, a registered module, before being started, is in IDLE s
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. |br|
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. |br|
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. |br|
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. |br|
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 and its enqueued pubsub messages are destroyed. |br|
STOPPED state is the same as IDLE state, but it means that module was started at least once and module's init() callback has already been called. |br|

module_start() needs to be called on a IDLE or STOPPED module. |br|
module_pause() needs to be called on a RUNNING module. |br|
Expand Down
14 changes: 14 additions & 0 deletions docs/src/memory.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.. |br| raw:: html

<br />

Memory
======

Libmodule, even in its _easy (high level) APIs, is targeting a smart and hopefully large audience. |br|
Consequently, it is up to developers to understand and observe any pointers' scope. |br|
It means that any data passed to its API is trusted; it's up to implementer to actually be sure that eg: a registered topic pointer stays alive until it is needed. |br|
This is often accomplished by using global/static variables or heap-allocations. |br|

Libmodule chose to minimize internal heap allocations to avoid performance issues or enforcing a behaviour to developers, that in my opinion is much worse. |br|
Note that instead map and stack APIs are more flexible about it, offering to eg: duplicate hashmap keys. |br|
36 changes: 36 additions & 0 deletions docs/src/module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,24 @@ Where not specified, these functions return a :ref:`module_ret_code <module_ret_
Returns _self handler for the module.

:returns: :c:type:`const self_t *`

.. c:macro:: m_load(path)
Attaches a new module from a .so file to "default" context. If module.so has a different context, this will be an error.

:param path: shared object path.
:param ctx_name: module's context name.
:type path: :c:type:`const char *`
:type ctx_name: :c:type:`const char *`

.. c:macro:: m_unload(path)
Detaches a module loaded from a .so file.

:param path: shared object path.
:param ctx_name: module's context name.
:type path: :c:type:`const char *`
:type ctx_name: :c:type:`const char *`

.. c:macro:: m_is(state)
Expand Down Expand Up @@ -247,6 +265,24 @@ Again, where not specified, these functions return a :ref:`module_ret_code <modu
:param self: pointer to module's handler. It is set to NULL after this call.
:type self: :c:type:`self_t **`

.. c:function:: m_load(path, ctx_name)
Attaches a new module from a .so file to ctx_name context. If module.so has a different context, this will be an error.

:param path: shared object path.
:param ctx_name: module's context name.
:type path: :c:type:`const char *`
:type ctx_name: :c:type:`const char *`

.. c:function:: m_unload(path, ctx_name)
Detaches a module loaded from a .so file.

:param path: shared object path.
:param ctx_name: module's context name.
:type path: :c:type:`const char *`
:type ctx_name: :c:type:`const char *`

.. c:function:: module_is(self, state)
Check current module's state
Expand Down

0 comments on commit 3125966

Please sign in to comment.