Skip to content
This repository was archived by the owner on Aug 19, 2021. It is now read-only.

Commit 68033ab

Browse files
committed
Updated documentation
- core api in equeue.h - porting api in equeue_tick/mutex/sema.h - README documentation - internal code documentation
1 parent 41e996f commit 68033ab

File tree

6 files changed

+342
-85
lines changed

6 files changed

+342
-85
lines changed

README.md

Lines changed: 169 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
## The equeue library ##
22

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

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

19-
// events are simple callbacks
19+
// events can be simple callbacks
2020
equeue_call(&queue, print, "called immediately");
2121
equeue_call_in(&queue, 2000, print, "called in 2 seconds");
2222
equeue_call_every(&queue, 1000, print, "called every 1 seconds");
2323

24-
// events are executed when dispatch is called
24+
// events are executed in equeue_dispatch
2525
equeue_dispatch(&queue, 3000);
2626

2727
print("called after 3 seconds");
2828

29-
// dispatch can be called in an infinite loop
29+
equeue_destroy(&queue);
30+
}
31+
```
32+
33+
The equeue library can be used as a normal event loop, or it can be
34+
backgrounded on a single hardware timer or even another event loop.
35+
The equeue library is both thread and irq safe, and provides functions
36+
for easily composing multiple queues.
37+
38+
The equeue library can act as a drop-in scheduler, provide synchronization
39+
between multiple threads, or just act as a mechanism for moving events
40+
out of interrupt contexts.
41+
42+
## Documentation ##
43+
44+
Unless it is elaborated, the in-depth documentation of the specific functions
45+
can be found in [equeue.h](equeue.h).
46+
47+
The core of the equeue library is the `equeue_t` type which represents a
48+
single event queue, and the `equeue_dispath` function which runs the equeue,
49+
providing the context for executing events.
50+
51+
On top of this, `equeue_call`, `equeue_call_in`, and `equeue_call_every`
52+
provide an easy method of posting events to be executed in the context
53+
of the `equeue_dispatch` function.
54+
55+
``` c
56+
#include "equeue.h"
57+
#include "game.h"
58+
59+
equeue_t queue;
60+
struct game game;
61+
62+
// button_isr may be in interrupt context
63+
void button_isr(void) {
64+
equeue_call(&queue, game_button_update, &game);
65+
}
66+
67+
// a simple user-interface framework
68+
int main() {
69+
equeue_create(&queue, 4096);
70+
game_create(&game);
71+
72+
// call game_screen_udpate at 60 Hz
73+
equeue_call_every(&queue, 1000/60, game_screen_update, &game);
74+
75+
// dispatch forever
76+
equeue_dispatch(&queue, -1);
77+
}
78+
```
79+
80+
In addition to simple events, an event can be manually allocated with
81+
`equeue_alloc` and posted with `equeue_post` to allow passing an arbitrary
82+
amount of data to the execution of the event. This memory is allocated out
83+
of the equeue's buffer, and dynamic memory can be completely avoided.
84+
85+
The equeue allocator is designed to minimize jitter in interrupt contexts as
86+
well as avoid memory fragmentation on small devices. The allocator achieves
87+
both constant-runtime and zero-fragmentation for fixed-size events, however
88+
grows linearly as the quantity of different sized allocations increases.
89+
90+
``` c
91+
#include "equeue.h"
92+
93+
equeue_t queue;
94+
95+
// arbitrary data can be moved to a different context
96+
int enet_callback(void *buffer, int size) {
97+
if (size > 512) {
98+
size = 512;
99+
}
100+
101+
void *event = equeue_alloc(&queue, 512);
102+
memcpy(event, buffer, size);
103+
equeue_post(&queue, event);
104+
105+
return size;
106+
}
107+
```
108+
109+
Additionally, in-flight events can be cancelled with `equeue_cancel`. Events
110+
are given unique ids on post, allowing safe cancellation of expired events.
111+
112+
``` c
113+
#include "equeue.h"
114+
115+
equeue_t queue;
116+
int sonar_value;
117+
int sonar_timeout_id;
118+
119+
void sonar_isr(int value) {
120+
equeue_cancel(&queue, sonar_timeout_id);
121+
sonar_value = value;
122+
}
123+
124+
void sonar_timeout(void *) {
125+
sonar_value = -1;
126+
}
127+
128+
void sonar_read(void) {
129+
sonar_timeout_id = equeue_call_in(&queue, 300, sonar_timeout, 0);
130+
sonar_start();
131+
}
132+
```
133+
134+
From an architectural standpoint, event queues easily align with module
135+
boundaries, where internal state can be implicitly synchronized through
136+
event registration. Multiple modules can easily use event queues running
137+
in separate threads.
138+
139+
Alternatively, multiple event queues can be easily composed through the
140+
`equeue_chain` function, which allows multiple event queues to share the
141+
context of a single `equeue_dispatch` call.
142+
143+
``` c
144+
#include "equeue.h"
145+
146+
// run a simultaneous localization and mapping loop in one queue
147+
struct slam {
148+
equeue_t queue;
149+
};
150+
151+
void slam_create(struct slam *s, equeue_t *target) {
152+
equeue_create(&s->queue, 4096);
153+
equeue_chain(&s->queue, target);
154+
equeue_call_every(&s->queue, 100, slam_filter);
155+
}
156+
157+
// run a sonar with it's own queue
158+
struct sonar {
159+
equeue_t equeue;
160+
struct slam *slam;
161+
};
162+
163+
void sonar_create(struct sonar *s, equeue_t *target) {
164+
equeue_create(&s->queue, 64);
165+
equeue_chain(&s->queue, target);
166+
equeue_call_in(&s->queue, 5, sonar_update, s);
167+
}
168+
169+
// although the above is perfectly synchronized, we can run these
170+
// modules on a single event queue
171+
int main() {
172+
equeue_t queue;
173+
equeue_create(&queue, 1024);
174+
175+
struct sonar s1, s2, s3;
176+
sonar_create(&s1, &queue);
177+
sonar_create(&s2, &queue);
178+
sonar_create(&s3, &queue);
179+
180+
struct slam slam;
181+
slam_create(&slam, &queue);
182+
183+
// dispatches events for all of the modules
30184
equeue_dispatch(&queue, -1);
31185
}
32186
```
33187
34-
The equeue library can be used for a normal event loops, however it also
35-
supports composition and multithreaded environments. More information on
36-
the idea behind composable event loops
37-
[here](https://gist.github.com/geky/4969d940f1bd5596bdc10e79093e2553).
188+
## Platform ##
189+
190+
The equeue library has a minimal porting layer that is flexible depending
191+
on the requirements of the underlying platform. Platform specific declarations
192+
and more information can be found in the following files:
193+
194+
- [equeue_tick](equeue_tick.h) - millisecond counter
195+
- [equeue_mutex](equeue_mutex.h) - non-recursive mutex
196+
- [equeue_sema](equeue_sema.h) - binary semaphore
197+
38198
39199
## Tests ##
40200
@@ -59,9 +219,3 @@ make prof | tee results.txt
59219
cat results.txt | make prof
60220
```
61221

62-
## Porting ##
63-
64-
The events library requires a small porting layer:
65-
- [equeue_tick](equeue_tick.h) - monotonic counter
66-
- [equeue_mutex](equeue_mutex.h) - non-recursive mutex
67-
- [equeue_sema](equeue_sema.h) - binary semaphore

0 commit comments

Comments
 (0)