Skip to content

Commit

Permalink
Updated docs + a couple of fixes.
Browse files Browse the repository at this point in the history
  • Loading branch information
FedeDP committed Jul 22, 2019
1 parent de43f79 commit 1893401
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 74 deletions.
27 changes: 11 additions & 16 deletions Lib/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ static module_ret_code _register_fd(module *mod, const int fd, const bool autocl
return MOD_ERR;
}

/* Linear search for fd */
/* Linearly search for fd */
static module_ret_code _deregister_fd(module *mod, const int fd) {
module_poll_t **tmp = &mod->fds;

Expand Down Expand Up @@ -149,6 +149,15 @@ static int manage_fds(module *mod, m_context *c, const int flag, const bool stop
module_poll_t *t = tmp;
tmp = tmp->prev;
if (flag == RM && stop) {
if (t->fd == mod->pubsub_fd[0]) {
/*
* Free all unread pubsub msg for this module.
*
* Pass a !NULL pointer as first parameter,
* so that flush_pubsub_msgs() will free messages instead of delivering them.
*/
flush_pubsub_msgs(mod, NULL, mod);
}
ret = _deregister_fd(mod, t->fd);
} else {
ret = poll_set_new_evt(t, c, flag);
Expand Down Expand Up @@ -212,27 +221,13 @@ module_ret_code stop(module *mod, const bool stop) {

GET_CTX_PRIV((&mod->self));

if (stop) {
/*
* Free all unread pubsub msg for this module.
* We need to do this every time module gets stopped,
* as if it is stopped and restarted multiple times,
* we will close and reopen its pubsub_fds,
* thus we are not able to retrieve its old messages anymore.
*
* Pass a !NULL pointer as first parameter,
* so flush_pubsub_msgs() will free messages instead of delivering them.
*/
flush_pubsub_msgs(mod, NULL, mod);
}

int ret = manage_fds(mod, c, RM, stop);
MOD_ASSERT(!ret, errors[stop], MOD_ERR);

mod->state = stop ? STOPPED : PAUSED;
/*
* When module gets stopped, its write-end pubsub fd is closed too
* Read-end is already closed by stop().
* Read-end is already closed by manage_fds().
*/
if (stop && mod->pubsub_fd[1] != -1) {
close(mod->pubsub_fd[1]);
Expand Down
7 changes: 5 additions & 2 deletions Lib/pubsub.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,11 @@ map_ret_code flush_pubsub_msgs(void *data, const char *key, void *value) {
* Actually tell msg ONLY if we are not deregistering module,
* ie: we are stopping looping on the context.
* Else, just free msg.
*
* While stopping module, manage_fds() will call this with data != NULL
* to let us know we should destroy all enqueued messages.
*/
if (!data) {
if (!data && _module_is(mod, RUNNING)) {
MODULE_DEBUG("Flushing enqueued pubsub message for module '%s'.\n", mod->name);
msg_t msg = { .is_pubsub = true, .ps_msg = &mm->msg };
run_pubsub_cb(mod, &msg);
Expand Down Expand Up @@ -247,7 +250,7 @@ module_ret_code module_broadcast(const self_t *self, const void *message,
module_ret_code module_poisonpill(const self_t *self, const self_t *recipient) {
GET_MOD(self);
GET_CTX(self);
MOD_PARAM_ASSERT(recipient);
MOD_PARAM_ASSERT(module_is(recipient, RUNNING));

return tell_system_pubsub_msg(recipient->mod, c, MODULE_POISONPILL, &mod->self, NULL);
}
14 changes: 8 additions & 6 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
- [x] Add a module_poisonpill() function, to send a system message to a module to stop it -> this will be enqueued in module's message queue,
conversely to module_stop that should stop module right away freeing all its enqueued messages.
- [x] Always destroy messages in flush_pubsub_msg()? RIght now it delivers all of them if we're stopping looping on ctx
- [x] module_poisonpill should only be sent to RUNNING modules
- [x] when stop looping on a context, flush all pubsub messages to RUNNING modules only. Destroy messages for non-running modules.

### Map
- [x] FIx: avoid incrementing map size on value update
Expand Down Expand Up @@ -72,12 +74,12 @@ conversely to module_stop that should stop module right away freeing all its enq
- [x] Avoid telling system messages like MODULE_STARTED/TOPIC_REGISTERED to ourselves
- [x] Document main() weak symbol!
- [x] Add a new page about trusting pointers
- [ ] modules_set_memhook/memhook_t
- [ ] new stop behaviour (stop will always destroy pubsub messages instead of delivering them, except when a module gets stopped through module_poisonpill)
- [ ] module_poisonpill
- [ ] New modules_loop behaviour (stop looping when no RUNNING modules)
- [ ] New Map API
- [ ] New Stack API
- [x] modules_set_memhook/memhook_t
- [x] new stop behaviour (stop will always destroy pubsub messages instead of delivering them, except when a module gets stopped through module_poisonpill)
- [x] module_poisonpill
- [x] New modules_loop behaviour (stop looping when no RUNNING modules)
- [x] New Map API
- [x] New Stack API

### Samples
- [x] Fix samples
Expand Down
18 changes: 9 additions & 9 deletions docs/src/data_structures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Types
enum module_states { IDLE = 0x1, RUNNING = 0x2, PAUSED = 0x4, STOPPED = 0x8 };
/* PubSub message types */
enum msg_type { USER, LOOP_STARTED, LOOP_STOPPED, TOPIC_REGISTERED, TOPIC_DEREGISTERED, MODULE_STARTED, MODULE_STOPPED };
enum msg_type { USER, LOOP_STARTED, LOOP_STOPPED, TOPIC_REGISTERED, TOPIC_DEREGISTERED, MODULE_STARTED, MODULE_STOPPED, MODULE_POISONPILL };
typedef struct {
const char *topic;
Expand All @@ -36,7 +36,7 @@ Types
} ps_msg_t;
typedef struct {
const int fd;
int fd;
const void *userptr;
} fd_msg_t;
Expand All @@ -49,14 +49,14 @@ Types
} msg_t;
/* Callbacks typedefs */
typedef void(*init_cb)(void);
typedef bool(*evaluate_cb)(void);
typedef void(*recv_cb)(const msg_t *const msg, const void *userdata);
typedef void(*destroy_cb)(void);
typedef void (*init_cb)(void);
typedef bool (*evaluate_cb)(void);
typedef void (*recv_cb)(const msg_t *const msg, const void *userdata);
typedef void (*destroy_cb)(void);
/* Logger callback */
typedef void (*log_cb)(const self_t *self, const char *fmt, va_list args, const void *userdata);
/* Memory management user-passed functions */
typedef void *(*malloc_fn)(size_t size);
typedef void *(*realloc_fn)(void *ptr, size_t size);
Expand All @@ -70,14 +70,14 @@ Types
recv_cb recv; // module's recv function
destroy_cb destroy; // module's destroy function
} userhook;
/* Struct that holds user defined memory functions */
typedef struct {
malloc_fn _malloc;
realloc_fn _realloc;
calloc_fn _calloc;
free_fn _free;
} memalloc_hook;
} memhook_t;
.. _module_ret_code:

Expand Down
1 change: 1 addition & 0 deletions docs/src/lifecycle.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ When module is started, thus reaching RUNNING state, all its registered fds will
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 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|
If you instead wish to stop a module letting it receive any enqueued pubsub message, you need to send a POISONPILL message to it, through module_poisonpill() function. |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
88 changes: 67 additions & 21 deletions docs/src/map.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ Structures
.. code::
typedef enum {
MAP_WRONG_PARAM = -5,
MAP_EPERM = -6,
MAP_WRONG_PARAM,
MAP_ERR,
MAP_MISSING,
MAP_FULL,
Expand All @@ -23,25 +24,83 @@ Structures
} map_ret_code;
/* Callback for map_iterate */
typedef map_ret_code (*map_cb)(void *, void *);
typedef map_ret_code (*map_cb)(void *, const char *, void *);
/* Fn for map_set_dtor */
typedef map_ret_code (*map_dtor)(void *);
typedef void (*map_dtor)(void *);
/* Incomplete struct declaration for hashmap */
typedef struct _map map_t;
/* Incomplete struct declaration for hashmap iterator */
typedef struct _map_itr map_itr_t;
API
---

Where not specified, these functions return a map_ret_code.

.. c:function:: map_new()
.. c:function:: map_new(const bool autofree, const map_dtor fn)
Create a new map_t object.

:param autofree: whether keys lifetime should be managed by map
:param fn: callback called on value destroy. If NULL, values won't be automatically destroyed.
:type autofree: :c:type:`const bool`
:type fn: :c:type:`const map_dtor`

:returns: pointer to newly allocated map_t.

.. c:function:: map_itr_new(m)
Create a new map iterator object.

:param m: pointer to map_t
:type m: :c:type:`const map_t *`

:returns: pointer to newly allocated map_itr_t.

.. c:function:: map_itr_next(itr)
Get next iterator.

:param itr: pointer to map_itr_t
:type itr: :c:type:`map_itr_t *`

:returns: pointer to next iterator.

.. c:function:: map_itr_remove(itr)
Remove current iterator {key, value} from map.

:param itr: pointer to map_itr_t
:type itr: :c:type:`map_itr_t *`

.. c:function:: map_itr_get_key(itr)
Get current iterator's key.

:param itr: pointer to map_itr_t
:type itr: :c:type:`const map_itr_t *`

:returns: current iterator's key

.. c:function:: map_itr_get_data(itr)
Get current iterator's data.

:param itr: pointer to map_itr_t
:type itr: :c:type:`const map_itr_t *`

:returns: current iterator's data

.. c:function:: map_itr_set_data(itr)
Set current iterator's data.

:param itr: pointer to map_itr_t
:type itr: :c:type:`const map_itr_t *`

.. c:function:: map_iterate(m, fn, userptr)
Iterate an hashmap calling cb on each element until MAP_OK is returned (or end of hashmap is reached). Returns MAP_MISSING if map is NULL.
Expand All @@ -53,20 +112,16 @@ Where not specified, these functions return a map_ret_code.
:type fn: :c:type:`const map_cb`
:type userptr: :c:type:`void *`

.. c:function:: map_put(m, key, val, dupkey, autofree)
.. c:function:: map_put(m, key, val)
Put a value inside hashmap. Note that if dupkey is true, key will be strdupped and its lifetime will be managed by libmodule.
Put a value inside hashmap.

:param m: pointer to map_t
:param key: key for this value
:param val: value to be put inside map
:param dupkey: whether to strdup key
:param autofree: whether to automatically free val upon map_remove/map_clear/map_free
:type m: :c:type:`map_t *`
:type key: :c:type:`const char *`
:type val: :c:type:`void *`
:type dupkey: :c:type:`const bool`
:type autofree: :c:type:`const bool`

.. c:function:: map_get(m, key)
Expand Down Expand Up @@ -99,7 +154,7 @@ Where not specified, these functions return a map_ret_code.

.. c:function:: map_clear(m)
Clears a map object by deleting any object inside map, and eventually freeing it too if marked with autofree.
Clears a map object by deleting any object inside map.

:param s: pointer to map_t
:type s: :c:type:`map_t *`
Expand All @@ -118,12 +173,3 @@ Where not specified, these functions return a map_ret_code.
:param m: pointer to map_t
:type m: :c:type:`map_t *`
:returns: map length or a map_ret_code if any error happens (map_t is null).

.. c:function:: map_set_dtor(m, fn)
Set a function to be called upon data deletion for autofree elements.

:param m: pointer to map_t
:param fn: pointer dtor callback
:type m: :c:type:`map_t *`
:type fn: :c:type:`map_dtor`
18 changes: 18 additions & 0 deletions docs/src/module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Where not specified, these functions return a :ref:`module_ret_code <module_ret_
.. c:macro:: m_stop(void)
Stop module's polling by closing its fds. Note that module is not destroyed: you can add new fds and call m_start on it.
Moreover, its enqueued pubsub messages are destroyed.

.. c:macro:: m_become(new_recv)
Expand Down Expand Up @@ -235,6 +236,13 @@ Where not specified, these functions return a :ref:`module_ret_code <module_ret_
:param global: whether to broadcast to every context.
:type msg: :c:type:`const char *`
:type global: :c:type:`const bool`

.. c:macro:: m_poisonpill(recipient)
Enqueue a POISONPILL message to recipient. This allows to stop another module after it flushes its pubsub messages.

:param recipient: RUNNING module to be stopped.
:type recipient: :c:type:`const self_t *`

.. _module_complex:

Expand Down Expand Up @@ -315,6 +323,7 @@ Again, where not specified, these functions return a :ref:`module_ret_code <modu
.. c:function:: module_stop(self)
Stop module's polling by closing its fds. Note that module is not destroyed: you can add new fds and call module_start on it.
Moreover, its enqueued pubsub messages are destroyed.

:param self: pointer to module's handler
:type self: :c:type:`const self_t *`
Expand Down Expand Up @@ -486,3 +495,12 @@ Again, where not specified, these functions return a :ref:`module_ret_code <modu
:type size: :c:type:`const ssize_t`
:type autofree: :c:type:`const bool`
:type global: :c:type:`const bool`

.. c:macro:: module_poisonpill(self, recipient)
Enqueue a POISONPILL message to recipient. This allows to stop another module after it flushes its pubsub messages.

:param self: pointer to module's handler
:param recipient: RUNNING module to be stopped.
:type self: :c:type:`const self_t *`
:type recipient: :c:type:`const self_t *`
7 changes: 5 additions & 2 deletions docs/src/modules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ Moreover, there are a bunch of functions that are common to every context (thus
:type argc: :c:type:`int`
:type argv: :c:type:`char *[]`

.. c:function:: modules_set_memalloc_hook(hook)
.. c:function:: modules_set_memhook(hook)
Set memory management functions. By default: malloc, realloc, calloc and free are used.

:param hook: new memory management hook.
:type hook: :c:type:`const memalloc_hook *`
:type hook: :c:type:`const memhook_t *`

Easy API
--------
Expand All @@ -53,6 +53,7 @@ It abstracts all of libmodule internals mechanisms to provide an easy-to-use and
.. c:macro:: modules_loop(void)
Start looping on events from modules. Note that as soon as modules_loop is called, a message with type == LOOP_STARTED will be broadcasted to all context's modules.
Moreover, loop is automatically stopped (with return code 0) if there are no more RUNNING modules in its context.

.. c:macro:: modules_quit(quit_code)
Expand Down Expand Up @@ -93,13 +94,15 @@ It exposes very similar functions to single-context API (again, single-context i
.. c:macro:: modules_ctx_loop(ctx_name)
Start looping on events from modules. Note that this is just a macro that calls modules_ctx_loop_events with MODULE_MAX_EVENTS (64) events.
Moreover, loop is automatically stopped (with return code 0) if there are no more RUNNING modules in its context.

:param ctx_name: context name.
:type ctx_name: :c:type:`const char *`

.. c:function:: modules_ctx_loop_events(ctx_name, maxevents)
Start looping on events from modules, on at most maxevents events at the same time. Note that as soon as modules_loop is called, a message with type == LOOP_STARTED will be broadcasted to all context's modules.
Moreover, loop is automatically stopped (with return code 0) if there are no more RUNNING modules in its context.

:param ctx_name: context name.
:param maxevents: max number of fds wakeup that will be managed at the same time.
Expand Down

0 comments on commit 1893401

Please sign in to comment.