Skip to content

Commit

Permalink
Updated documentation
Browse files Browse the repository at this point in the history
- core api in equeue.h
- porting api in equeue_tick/mutex/sema.h
- README documentation
- internal code documentation
  • Loading branch information
geky committed Aug 9, 2016
1 parent 41e996f commit 68033ab
Show file tree
Hide file tree
Showing 6 changed files with 342 additions and 85 deletions.
184 changes: 169 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## The equeue library ##

The equeue library provides a composable event queue implementation
that acts as a drop in scheduler and event framework.
The equeue library is designed as a simple but powerful library for scheduling
events on composable event queues.

``` c
#include "equeue.h"
Expand All @@ -16,25 +16,185 @@ int main() {
equeue_t queue;
equeue_create(&queue, 32*EQUEUE_EVENT_SIZE);

// events are simple callbacks
// events can be simple callbacks
equeue_call(&queue, print, "called immediately");
equeue_call_in(&queue, 2000, print, "called in 2 seconds");
equeue_call_every(&queue, 1000, print, "called every 1 seconds");

// events are executed when dispatch is called
// events are executed in equeue_dispatch
equeue_dispatch(&queue, 3000);

print("called after 3 seconds");

// dispatch can be called in an infinite loop
equeue_destroy(&queue);
}
```
The equeue library can be used as a normal event loop, or it can be
backgrounded on a single hardware timer or even another event loop.
The equeue library is both thread and irq safe, and provides functions
for easily composing multiple queues.
The equeue library can act as a drop-in scheduler, provide synchronization
between multiple threads, or just act as a mechanism for moving events
out of interrupt contexts.
## Documentation ##
Unless it is elaborated, the in-depth documentation of the specific functions
can be found in [equeue.h](equeue.h).
The core of the equeue library is the `equeue_t` type which represents a
single event queue, and the `equeue_dispath` function which runs the equeue,
providing the context for executing events.
On top of this, `equeue_call`, `equeue_call_in`, and `equeue_call_every`
provide an easy method of posting events to be executed in the context
of the `equeue_dispatch` function.
``` c
#include "equeue.h"
#include "game.h"
equeue_t queue;
struct game game;
// button_isr may be in interrupt context
void button_isr(void) {
equeue_call(&queue, game_button_update, &game);
}
// a simple user-interface framework
int main() {
equeue_create(&queue, 4096);
game_create(&game);
// call game_screen_udpate at 60 Hz
equeue_call_every(&queue, 1000/60, game_screen_update, &game);
// dispatch forever
equeue_dispatch(&queue, -1);
}
```

In addition to simple events, an event can be manually allocated with
`equeue_alloc` and posted with `equeue_post` to allow passing an arbitrary
amount of data to the execution of the event. This memory is allocated out
of the equeue's buffer, and dynamic memory can be completely avoided.

The equeue allocator is designed to minimize jitter in interrupt contexts as
well as avoid memory fragmentation on small devices. The allocator achieves
both constant-runtime and zero-fragmentation for fixed-size events, however
grows linearly as the quantity of different sized allocations increases.

``` c
#include "equeue.h"

equeue_t queue;

// arbitrary data can be moved to a different context
int enet_callback(void *buffer, int size) {
if (size > 512) {
size = 512;
}

void *event = equeue_alloc(&queue, 512);
memcpy(event, buffer, size);
equeue_post(&queue, event);

return size;
}
```
Additionally, in-flight events can be cancelled with `equeue_cancel`. Events
are given unique ids on post, allowing safe cancellation of expired events.
``` c
#include "equeue.h"
equeue_t queue;
int sonar_value;
int sonar_timeout_id;
void sonar_isr(int value) {
equeue_cancel(&queue, sonar_timeout_id);
sonar_value = value;
}
void sonar_timeout(void *) {
sonar_value = -1;
}
void sonar_read(void) {
sonar_timeout_id = equeue_call_in(&queue, 300, sonar_timeout, 0);
sonar_start();
}
```

From an architectural standpoint, event queues easily align with module
boundaries, where internal state can be implicitly synchronized through
event registration. Multiple modules can easily use event queues running
in separate threads.

Alternatively, multiple event queues can be easily composed through the
`equeue_chain` function, which allows multiple event queues to share the
context of a single `equeue_dispatch` call.

``` c
#include "equeue.h"

// run a simultaneous localization and mapping loop in one queue
struct slam {
equeue_t queue;
};

void slam_create(struct slam *s, equeue_t *target) {
equeue_create(&s->queue, 4096);
equeue_chain(&s->queue, target);
equeue_call_every(&s->queue, 100, slam_filter);
}

// run a sonar with it's own queue
struct sonar {
equeue_t equeue;
struct slam *slam;
};

void sonar_create(struct sonar *s, equeue_t *target) {
equeue_create(&s->queue, 64);
equeue_chain(&s->queue, target);
equeue_call_in(&s->queue, 5, sonar_update, s);
}

// although the above is perfectly synchronized, we can run these
// modules on a single event queue
int main() {
equeue_t queue;
equeue_create(&queue, 1024);

struct sonar s1, s2, s3;
sonar_create(&s1, &queue);
sonar_create(&s2, &queue);
sonar_create(&s3, &queue);

struct slam slam;
slam_create(&slam, &queue);

// dispatches events for all of the modules
equeue_dispatch(&queue, -1);
}
```
The equeue library can be used for a normal event loops, however it also
supports composition and multithreaded environments. More information on
the idea behind composable event loops
[here](https://gist.github.com/geky/4969d940f1bd5596bdc10e79093e2553).
## Platform ##
The equeue library has a minimal porting layer that is flexible depending
on the requirements of the underlying platform. Platform specific declarations
and more information can be found in the following files:
- [equeue_tick](equeue_tick.h) - millisecond counter
- [equeue_mutex](equeue_mutex.h) - non-recursive mutex
- [equeue_sema](equeue_sema.h) - binary semaphore
## Tests ##
Expand All @@ -59,9 +219,3 @@ make prof | tee results.txt
cat results.txt | make prof
```

## Porting ##

The events library requires a small porting layer:
- [equeue_tick](equeue_tick.h) - monotonic counter
- [equeue_mutex](equeue_mutex.h) - non-recursive mutex
- [equeue_sema](equeue_sema.h) - binary semaphore
Loading

0 comments on commit 68033ab

Please sign in to comment.