Skip to content

Commit

Permalink
Added a ts field to m_evt_t.
Browse files Browse the repository at this point in the history
Moreover, moved CMAKE_C_FLAGS set to root CMakeLists and fixed some warnings.

Initially added some new doc pages.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
  • Loading branch information
FedeDP committed Feb 4, 2022
1 parent f29315e commit ef66004
Show file tree
Hide file tree
Showing 33 changed files with 277 additions and 36 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ endif()

set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")

set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -std=c11 -Wtype-limits -Wstrict-overflow -fno-strict-aliasing -Wformat -Wformat-security -fsanitize=undefined")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")

option(BUILD_TESTS "build ${PROJECT_NAME} tests" OFF)
if(BUILD_TESTS)
find_package(Cmocka)
Expand Down
3 changes: 0 additions & 3 deletions Lib/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,6 @@ install(FILES ${CMAKE_BINARY_DIR}/libmodule_core.pc
install(FILES LICENSE
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/licenses/lib${PROJECT_NAME})

set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -std=c11 -Wtype-limits -Wstrict-overflow -fno-strict-aliasing -Wformat -Wformat-security -fsanitize=undefined")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")

# Packaging support
# THANKS to libkqueue CMakeLists.txt for packaging support :)
SET(CPACK_SET_DESTDIR "on")
Expand Down
1 change: 1 addition & 0 deletions Lib/core/ctx.c
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ static int recv_events(m_ctx_t *c, int timeout) {
m_evt_t *msg = NULL;
if (evt) {
msg = &evt->evt;
fetch_ms(&msg->ts, NULL);
M_INFO("'%s' received %u type msg.\n", mod->name, msg->type);
switch (msg->type) {
case M_SRC_TYPE_FD:
Expand Down
6 changes: 3 additions & 3 deletions Lib/core/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ _public_ _weak_ void m_ctx_post_loop(m_ctx_t *c, int argc, char *argv[]) {

_public_ _weak_ int main(int argc, char *argv[]) {
if (!default_ctx) {
fprintf(stderr, "No context available.\n");
M_ERR("No context available.\n");
return EXIT_FAILURE;
}
m_ctx_pre_loop(default_ctx, argc, argv);
Expand All @@ -42,14 +42,14 @@ _public_ _weak_ int main(int argc, char *argv[]) {
}

static _m_ctor1_ void libmodule_init(void) {
M_DEBUG("Initializing libmodule %d.%d.%d.\n", LIBMODULE_VERSION_MAJ, LIBMODULE_VERSION_MIN, LIBMODULE_VERSION_PAT);
M_INFO("Initializing libmodule %d.%d.%d.\n", LIBMODULE_VERSION_MAJ, LIBMODULE_VERSION_MIN, LIBMODULE_VERSION_PAT);
ctx = m_map_new(0, mem_dtor);
assert(ctx != NULL);
pthread_mutex_init(&mx, NULL);
}

static _m_dtor0_ void libmodule_deinit(void) {
M_DEBUG("Destroying libmodule.\n");
M_INFO("Destroying libmodule.\n");
m_map_free(&ctx);
pthread_mutex_destroy(&mx);
}
Expand Down
2 changes: 2 additions & 0 deletions Lib/core/public/module/cmn.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <sys/types.h>

#define LIBMODULE_VERSION_MAJ @PROJECT_VERSION_MAJOR@
#define LIBMODULE_VERSION_MIN @PROJECT_VERSION_MINOR@
Expand Down
1 change: 0 additions & 1 deletion Lib/core/public/module/ctx.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#ifndef LIBMODULE_CORE_H
#define LIBMODULE_CORE_H
#endif

#include <module/cmn.h>

/* Context flags */
Expand Down
6 changes: 2 additions & 4 deletions Lib/core/public/module/mod.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
#define LIBMODULE_CORE_H
#endif

#include <stdbool.h>
#include <time.h>
#include <sys/types.h>
#include <module/cmn.h>
#include <module/structs/itr.h>

Expand Down Expand Up @@ -137,7 +134,7 @@ typedef struct {
/* Libmodule receive() main message type */
typedef struct {
m_src_types type; // Event type
union { // Events
union { // Event data
m_evt_fd_t *fd_evt;
m_evt_ps_t *ps_evt;
m_evt_tmr_t *tmr_evt;
Expand All @@ -148,6 +145,7 @@ typedef struct {
m_evt_thresh_t *thresh_evt;
};
const void *userdata; // Event userdata, passed through m_mod_src_register()
uint64_t ts; // Event timestamp
} m_evt_t;

/* Modules states */
Expand Down
1 change: 0 additions & 1 deletion Lib/core/public/module/plugin_C.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#pragma once

#include <module/plugin.h>
#include <module/mod.h>

/**
Expand Down
6 changes: 4 additions & 2 deletions Lib/thpool/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.1)

find_package (Threads)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads REQUIRED)

file(GLOB PUBLIC_H_thpool Lib/thpool/public/module/thpool/*.h)
file(GLOB SRCS_thpool Lib/thpool/*.c)
Expand Down
1 change: 1 addition & 0 deletions Lib/thpool/public/module/thpool/thpool.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>

typedef void *(*m_thpool_task)(void *);

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Libmodule

[![builds.sr.ht status](https://builds.sr.ht/~fededp/libmodule.svg)](https://builds.sr.ht/~fededp/libmodule?)
[![Documentation Status](https://readthedocs.org/projects/libmodule/badge/?version=latest)](http://libmodule.readthedocs.io/en/latest/?badge=latest)
[![Documentation Status](https://readthedocs.org/projects/libmodule/badge/?version=stable)](http://libmodule.readthedocs.io/en/latest/?badge=stable)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## What is this?
Expand All @@ -12,7 +12,7 @@ Indeed, libmodule was heavily inspired by my own actor library experience with [
## What is a module, anyway?

Unsurprisingly, module is the core concept of libmodule architecture.
A module is an Actor that can listen on socket events too.
A module is an Actor that can listen on non-pubsub events too.
Frankly speaking, it is denoted by a M_MOD() macro plus a bunch of mandatory callbacks, eg:
```C
#include <module/mod_easy.h>
Expand Down Expand Up @@ -68,7 +68,7 @@ static void m_mod_on_evt(m_mod_t *mod, const m_queue_t *const evts) {
}
}
```
In this example, a "Pippo" module is created and will echo chars from stdin until 'q' is pressed.
In this example, a "Pippo" module is created and will echo chars from stdin until 'q' is pressed, exiting with 0.
Note that it does not even need a main function, as libmodule already provides a default one as a [weak, thus overridable, symbol](https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Function-Attributes.html).
## Is it portable?
Expand Down
4 changes: 4 additions & 0 deletions Samples/Easy/pippo.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#define _POSIX_C_SOURCE 199309L

#include <module/mod_easy.h>
#include <module/mem/mem.h>
#include <module/ctx.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <time.h>

#ifdef __linux__
#include <sys/inotify.h>
#else
Expand Down
3 changes: 3 additions & 0 deletions Samples/Task/pippo.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#define _POSIX_C_SOURCE 199309L

#include <module/mod_easy.h>
#include <module/ctx.h>
#include <unistd.h>
#include <time.h>

M_MOD("Pippo");

Expand Down
2 changes: 2 additions & 0 deletions Samples/Thpool/main.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#define _POSIX_C_SOURCE 200809L

#include <module/thpool/thpool.h>
#include <string.h>
#include <stdlib.h>
Expand Down
4 changes: 2 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ Maybe try to m_mod_load() the newly created file?

- [x] Use attribute cleanup where it makes sense

- [ ] expose a set.h internally using map.c APi (from the same source file)

- [x] when timerfd fires or batch size is reached, all Batched events will be sent
- [x] m_mod_set_batch_time() -> m_src_timer_register( M_SRC_INTERNAL) ...
- [x] New, internal flag: M_SRC_INTERNAL -> used for internal flags
Expand Down Expand Up @@ -78,6 +76,8 @@ Maybe try to m_mod_load() the newly created file?

- [x] Drop m_mod_ps_broadcast to reduce API, just call m_mod_ps_publish with NULL topic

- [x] add a timestamp to m_evt_t

### Last changes

#### Plugin API
Expand Down
35 changes: 35 additions & 0 deletions docs/concepts/concepts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Concepts

## Naming Conventions

Libmodule uses the following naming conventions:
* `m_` is the prefix for all the library API
* `m_foo_` APIs group together the same namespace API
* struct types have the `_t` suffix
* enum types **do not have** the `_t` suffix

## Logging

Libmodule offers an internal logging facility, to enable verbose logging for the library.
There are 4 log levels that can be enabled through `LIBMODULE_LOG` env variable:
* 0 (error, **default**)
* 1 (warn)
* 2 (info)
* 3 (debug)

Moreover, you can specify an output file for the log, by passing `LIBMODULE_LOG_OUTPUT` env variable.
By default, stdout is used.

## Memory

### Ref counted

Ideally, all of the exposed pointers have their lifetime reference based.
This means that you can call `m_mem_ref()` API to manage eg: `m_mod_t`, `m_ctx_t`, `m_evt_t` pointers, and so on.
Normally, there is no such need because the library manages everything.
But if you called `m_mod_ref()`, then you own a reference on that module and it's up to you to free the reference by calling `m_mem_unref()` on it.

### Memhook

Moreover, libmodule allows users to override default memhook used, by calling `m_set_memhook()`.
This function must be called from `m_pre_start()` function, because it has to be called before any internal ctor is run, ie: before any allocation takes place.
25 changes: 25 additions & 0 deletions docs/concepts/ctx.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Context

A context can be seen as a collector for modules. You can loop on events from each context, and each context behaves independently from others.
This can be particularly useful when dealing with 2+ threads; ideally, each thread has its own module's context and thus its own events to be polled.
Modules can only see and reach (through PubSub messaging) other modules from same context.

## Loop

Libmodule offers an internal loop, started with `m_ctx_loop()`; note that each context has its own loop.
Moreover, you can even easily integrate it into your own loop: `m_ctx_get_fd()` will retrieve a pollable fd and POLLIN events will be raised whenever a new message is available.
Remember that before starting your loop, `m_ctx_dispatch()` should be called, to dispatch initial "LoopStarted" messages to each module.
Then, whenever POLLIN data is available on libmodule's fd, you only need to call m_ctx_dispatch() again.
Finally, remember to close() libmodule's fd retrieved through `m_ctx_get_fd()`.

## Default main

Libmodule offers a single-context looping main as a weak, thus overridable, symbol.
This means that developers must not even create a main for simple applications.

Default main is mostly useful in conjunction with `<module/mod_easy>` API.
It offers 2 functions that can be implemented by caller:

* `m_ctx_pre_loop(m_ctx_t *c, int argc, char *argv[])` that is called before the ctx loop is started
* `m_ctx_post_loop(m_ctx_t *c, int argc, char *argv[])` that is called after the loop stopped, right before leaving

65 changes: 65 additions & 0 deletions docs/concepts/mod.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Module

A module is the core entity of libmodule: it is a single and indipendent logical unit that reacts to events.
It can be seen as an Actor with the ability of managing non-PubSub events.
It requires some callbacks that are used by libmodule to manage its life.

## Source

Each module's listens to events by registering event sources.
A source can be a timer, a topic (for PubSub communications between modules), a signal, a socket, etc etc.
You can find the list of supported sources in `<module/mod.h>`.

### Source Priorities

TODO

## Lifecycle

### Callbacks

First of all, module lifecycle is automatically managed by libmodule; moreover, when using `<module/mod_easy>` API,
module registration/deregistration is completely automated and transparent to developer.
This means that when using it, you will only have to declare a source file as a module and define needed functions.

First function to be called is `m_pre_start()`; it is called right after libmodule gets linked.
This function is useful to set a custom memhook for libmodule, through `m_set_memhook()` API.

> **_EASY API_**: each module's `m_mod_pre_start()` function is called. At this stage, no module is registered yet.
Finally, libmodule will register every module.
Once a module is registered, it will be initially set to an M_MOD_IDLE state. Idle means that the module is not started yet, thus it cannot receive any event.

As soon as its context starts looping, `m_mod_on_eval()` function will be called on every M_MOD_IDLE module, trying to start it right away.
That function will be called at each state machine change, for each idle module, until it returns true.

As soon as `m_mod_on_eval()` returns true, a module is started. It means its `m_mod_on_start()` function is finally called and its state is set to M_MOD_RUNNING.
When a module reaches RUNNING state, its context loop will finally receive events for its registered sources.

Whenever an event triggers a module's wakeup, its `m_mod_on_evt()` callback can be called (depending on the event source priority) with a `m_queue_t<m_evt_t *>` argument.

Finally, when stopping a module, its `m_mod_on_stop()` function is called.
Note that a module is automatically stopped during the process of module's deregistration.

Thus, for Easy API, you should only implement m_mod_on_eval() to return true when it is ready to be started,
then eventually register event sources in m_mod_on_start(), and manage events in m_mod_on_evt().
If you need to cleanup any data, manage it in m_mod_on_stop().

### States

As previously mentioned, a registered module, before being started, is in M_MOD_IDLE state.
M_MOD_IDLE state means that it has never been started before; it won't receive any event thus its m_evt_cb will never be called.
When module is started, thus reaching M_MOD_RUNNING state, all its registered sources will finally start to receive events. Sources registered while in M_MOD_RUNNING state are automatically polled.
If a module is paused, reaching M_MOD_PAUSED state, it will stop polling on its event sources, but event sources will still be alive, just not polled. Thus, as soon as module is resumed, all events received during PAUSED state will trigger m_evt_cb.
If a module gets stopped, reaching M_MOD_STOPPED state, it will destroy any registered source and it will be resetted to its initial state.
If you instead wish to stop a module letting it manage any already-enqueued event, you need to send a POISONPILL message to it, through `m_mod_poisonpill()` API.
The difference between M_MOD_IDLE and M_MOD_STOPPED states is that M_MOD_IDLE modules will be evaluated to be started during context loop, while M_MOD_STOPPED modules won't.
When a module is deregistered, it will reach a final M_MOD_ZOMBIE state. It means that the module is no more reachable neither usable, but it can still be referenced by any previously sent message (or any `m_mod_ref()`).
After all module's ref count drops to 0 (ie: all sent messages are received by respective recipients and there are no pending `m_mod_unref()`) module will finally get destroyed and its memory freed.
You can only call `m_mod_is()`, `m_mod_name()` and `m_mod_ctx()` on a zombie module.

To summarize:
* `m_mod_start()` needs to be called on an M_MOD_IDLE or M_MOD_STOPPED module.
* `m_mod_pause()` needs to be called on a M_MOD_RUNNING module.
* `m_mod_resume()` needs to be called on a M_MOD_PAUSED module.
* `m_mod_stop()` needs to be called on a M_MOD_RUNNING or M_MOD_PAUSED module.
12 changes: 12 additions & 0 deletions docs/core/core.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Libmodule core API

Libmodule core API denotes the set of symbols exposed by `libmodule_core.so` objects, whose headers are installed in $includedir/module/.

It is made of various headers:
* `mod.h` that contains module related API
* `mod_easy.h` that contains an helper macro to build simplest applications, ie: single context, single module for source file
* `ctx.h` that contains context related API
* `fs.h` (installed only if `WITH_FS` cmake option is enabled) that contains libmodule fuseFS related API
* `cmn.h` that contains some common symbols, and cannot be manually included
* `plugin.h` that contains libmodule plugin API
* `plugin_C.h` that contains a small C sdk (ie: a macro) to build a C plugin
1 change: 1 addition & 0 deletions docs/core/ctx.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

1 change: 1 addition & 0 deletions docs/core/mod.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

0 comments on commit ef66004

Please sign in to comment.