Marco Zecchini
/
Example_RTOS
Rtos API example
mbed-os/events/equeue/README.md@0:9fca2b23d0ba, 2019-02-23 (annotated)
- Committer:
- marcozecchini
- Date:
- Sat Feb 23 12:13:36 2019 +0000
- Revision:
- 0:9fca2b23d0ba
final commit
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
marcozecchini | 0:9fca2b23d0ba | 1 | ## The equeue library ## |
marcozecchini | 0:9fca2b23d0ba | 2 | |
marcozecchini | 0:9fca2b23d0ba | 3 | The equeue library is designed as a simple but powerful library for scheduling |
marcozecchini | 0:9fca2b23d0ba | 4 | events on composable queues. |
marcozecchini | 0:9fca2b23d0ba | 5 | |
marcozecchini | 0:9fca2b23d0ba | 6 | ``` c |
marcozecchini | 0:9fca2b23d0ba | 7 | #include "equeue.h" |
marcozecchini | 0:9fca2b23d0ba | 8 | #include <stdio.h> |
marcozecchini | 0:9fca2b23d0ba | 9 | |
marcozecchini | 0:9fca2b23d0ba | 10 | int main() { |
marcozecchini | 0:9fca2b23d0ba | 11 | // creates a queue with space for 32 basic events |
marcozecchini | 0:9fca2b23d0ba | 12 | equeue_t queue; |
marcozecchini | 0:9fca2b23d0ba | 13 | equeue_create(&queue, 32*EQUEUE_EVENT_SIZE); |
marcozecchini | 0:9fca2b23d0ba | 14 | |
marcozecchini | 0:9fca2b23d0ba | 15 | // events can be simple callbacks |
marcozecchini | 0:9fca2b23d0ba | 16 | equeue_call(&queue, print, "called immediately"); |
marcozecchini | 0:9fca2b23d0ba | 17 | equeue_call_in(&queue, 2000, print, "called in 2 seconds"); |
marcozecchini | 0:9fca2b23d0ba | 18 | equeue_call_every(&queue, 1000, print, "called every 1 seconds"); |
marcozecchini | 0:9fca2b23d0ba | 19 | |
marcozecchini | 0:9fca2b23d0ba | 20 | // events are executed in equeue_dispatch |
marcozecchini | 0:9fca2b23d0ba | 21 | equeue_dispatch(&queue, 3000); |
marcozecchini | 0:9fca2b23d0ba | 22 | |
marcozecchini | 0:9fca2b23d0ba | 23 | print("called after 3 seconds"); |
marcozecchini | 0:9fca2b23d0ba | 24 | |
marcozecchini | 0:9fca2b23d0ba | 25 | equeue_destroy(&queue); |
marcozecchini | 0:9fca2b23d0ba | 26 | } |
marcozecchini | 0:9fca2b23d0ba | 27 | ``` |
marcozecchini | 0:9fca2b23d0ba | 28 | |
marcozecchini | 0:9fca2b23d0ba | 29 | The equeue library can be used as a normal event loop, or it can be |
marcozecchini | 0:9fca2b23d0ba | 30 | backgrounded on a single hardware timer or even another event loop. It |
marcozecchini | 0:9fca2b23d0ba | 31 | is both thread and irq safe, and provides functions for easily composing |
marcozecchini | 0:9fca2b23d0ba | 32 | multiple queues. |
marcozecchini | 0:9fca2b23d0ba | 33 | |
marcozecchini | 0:9fca2b23d0ba | 34 | The equeue library can act as a drop-in scheduler, provide synchronization |
marcozecchini | 0:9fca2b23d0ba | 35 | between multiple threads, or just act as a mechanism for moving events |
marcozecchini | 0:9fca2b23d0ba | 36 | out of interrupt contexts. |
marcozecchini | 0:9fca2b23d0ba | 37 | |
marcozecchini | 0:9fca2b23d0ba | 38 | ## Documentation ## |
marcozecchini | 0:9fca2b23d0ba | 39 | |
marcozecchini | 0:9fca2b23d0ba | 40 | The in-depth documentation on specific functions can be found in |
marcozecchini | 0:9fca2b23d0ba | 41 | [equeue.h](equeue.h). |
marcozecchini | 0:9fca2b23d0ba | 42 | |
marcozecchini | 0:9fca2b23d0ba | 43 | The core of the equeue library is the `equeue_t` type which represents a |
marcozecchini | 0:9fca2b23d0ba | 44 | single event queue, and the `equeue_dispatch` function which runs the equeue, |
marcozecchini | 0:9fca2b23d0ba | 45 | providing the context for executing events. |
marcozecchini | 0:9fca2b23d0ba | 46 | |
marcozecchini | 0:9fca2b23d0ba | 47 | On top of this, `equeue_call`, `equeue_call_in`, and `equeue_call_every` |
marcozecchini | 0:9fca2b23d0ba | 48 | provide easy methods for posting events to execute in the context of the |
marcozecchini | 0:9fca2b23d0ba | 49 | `equeue_dispatch` function. |
marcozecchini | 0:9fca2b23d0ba | 50 | |
marcozecchini | 0:9fca2b23d0ba | 51 | ``` c |
marcozecchini | 0:9fca2b23d0ba | 52 | #include "equeue.h" |
marcozecchini | 0:9fca2b23d0ba | 53 | #include "game.h" |
marcozecchini | 0:9fca2b23d0ba | 54 | |
marcozecchini | 0:9fca2b23d0ba | 55 | equeue_t queue; |
marcozecchini | 0:9fca2b23d0ba | 56 | struct game game; |
marcozecchini | 0:9fca2b23d0ba | 57 | |
marcozecchini | 0:9fca2b23d0ba | 58 | // button_isr may be in interrupt context |
marcozecchini | 0:9fca2b23d0ba | 59 | void button_isr(void) { |
marcozecchini | 0:9fca2b23d0ba | 60 | equeue_call(&queue, game_button_update, &game); |
marcozecchini | 0:9fca2b23d0ba | 61 | } |
marcozecchini | 0:9fca2b23d0ba | 62 | |
marcozecchini | 0:9fca2b23d0ba | 63 | // a simple user-interface framework |
marcozecchini | 0:9fca2b23d0ba | 64 | int main() { |
marcozecchini | 0:9fca2b23d0ba | 65 | equeue_create(&queue, 4096); |
marcozecchini | 0:9fca2b23d0ba | 66 | game_create(&game); |
marcozecchini | 0:9fca2b23d0ba | 67 | |
marcozecchini | 0:9fca2b23d0ba | 68 | // call game_screen_udpate at 60 Hz |
marcozecchini | 0:9fca2b23d0ba | 69 | equeue_call_every(&queue, 1000/60, game_screen_update, &game); |
marcozecchini | 0:9fca2b23d0ba | 70 | |
marcozecchini | 0:9fca2b23d0ba | 71 | // dispatch forever |
marcozecchini | 0:9fca2b23d0ba | 72 | equeue_dispatch(&queue, -1); |
marcozecchini | 0:9fca2b23d0ba | 73 | } |
marcozecchini | 0:9fca2b23d0ba | 74 | ``` |
marcozecchini | 0:9fca2b23d0ba | 75 | |
marcozecchini | 0:9fca2b23d0ba | 76 | In addition to simple callbacks, an event can be manually allocated with |
marcozecchini | 0:9fca2b23d0ba | 77 | `equeue_alloc` and posted with `equeue_post` to allow passing an arbitrary |
marcozecchini | 0:9fca2b23d0ba | 78 | amount of context to the execution of the event. This memory is allocated out |
marcozecchini | 0:9fca2b23d0ba | 79 | of the equeue's buffer, and dynamic memory can be completely avoided. |
marcozecchini | 0:9fca2b23d0ba | 80 | |
marcozecchini | 0:9fca2b23d0ba | 81 | The equeue allocator is designed to minimize jitter in interrupt contexts as |
marcozecchini | 0:9fca2b23d0ba | 82 | well as avoid memory fragmentation on small devices. The allocator achieves |
marcozecchini | 0:9fca2b23d0ba | 83 | both constant-runtime and zero-fragmentation for fixed-size events, however |
marcozecchini | 0:9fca2b23d0ba | 84 | grows linearly as the quantity of differently-sized allocations increases. |
marcozecchini | 0:9fca2b23d0ba | 85 | |
marcozecchini | 0:9fca2b23d0ba | 86 | ``` c |
marcozecchini | 0:9fca2b23d0ba | 87 | #include "equeue.h" |
marcozecchini | 0:9fca2b23d0ba | 88 | |
marcozecchini | 0:9fca2b23d0ba | 89 | equeue_t queue; |
marcozecchini | 0:9fca2b23d0ba | 90 | |
marcozecchini | 0:9fca2b23d0ba | 91 | // arbitrary data can be moved to a different context |
marcozecchini | 0:9fca2b23d0ba | 92 | int enet_consume(void *buffer, int size) { |
marcozecchini | 0:9fca2b23d0ba | 93 | if (size > 512) { |
marcozecchini | 0:9fca2b23d0ba | 94 | size = 512; |
marcozecchini | 0:9fca2b23d0ba | 95 | } |
marcozecchini | 0:9fca2b23d0ba | 96 | |
marcozecchini | 0:9fca2b23d0ba | 97 | void *data = equeue_alloc(&queue, 512); |
marcozecchini | 0:9fca2b23d0ba | 98 | memcpy(data, buffer, size); |
marcozecchini | 0:9fca2b23d0ba | 99 | equeue_post(&queue, handle_data_elsewhere, data); |
marcozecchini | 0:9fca2b23d0ba | 100 | |
marcozecchini | 0:9fca2b23d0ba | 101 | return size; |
marcozecchini | 0:9fca2b23d0ba | 102 | } |
marcozecchini | 0:9fca2b23d0ba | 103 | ``` |
marcozecchini | 0:9fca2b23d0ba | 104 | |
marcozecchini | 0:9fca2b23d0ba | 105 | Additionally, in-flight events can be cancelled with `equeue_cancel`. Events |
marcozecchini | 0:9fca2b23d0ba | 106 | are given unique ids on post, allowing safe cancellation of expired events. |
marcozecchini | 0:9fca2b23d0ba | 107 | |
marcozecchini | 0:9fca2b23d0ba | 108 | ``` c |
marcozecchini | 0:9fca2b23d0ba | 109 | #include "equeue.h" |
marcozecchini | 0:9fca2b23d0ba | 110 | |
marcozecchini | 0:9fca2b23d0ba | 111 | equeue_t queue; |
marcozecchini | 0:9fca2b23d0ba | 112 | int sonar_value; |
marcozecchini | 0:9fca2b23d0ba | 113 | int sonar_timeout_id; |
marcozecchini | 0:9fca2b23d0ba | 114 | |
marcozecchini | 0:9fca2b23d0ba | 115 | void sonar_isr(int value) { |
marcozecchini | 0:9fca2b23d0ba | 116 | equeue_cancel(&queue, sonar_timeout_id); |
marcozecchini | 0:9fca2b23d0ba | 117 | sonar_value = value; |
marcozecchini | 0:9fca2b23d0ba | 118 | } |
marcozecchini | 0:9fca2b23d0ba | 119 | |
marcozecchini | 0:9fca2b23d0ba | 120 | void sonar_timeout(void *) { |
marcozecchini | 0:9fca2b23d0ba | 121 | sonar_value = -1; |
marcozecchini | 0:9fca2b23d0ba | 122 | } |
marcozecchini | 0:9fca2b23d0ba | 123 | |
marcozecchini | 0:9fca2b23d0ba | 124 | void sonar_read(void) { |
marcozecchini | 0:9fca2b23d0ba | 125 | sonar_timeout_id = equeue_call_in(&queue, 300, sonar_timeout, 0); |
marcozecchini | 0:9fca2b23d0ba | 126 | sonar_start(); |
marcozecchini | 0:9fca2b23d0ba | 127 | } |
marcozecchini | 0:9fca2b23d0ba | 128 | ``` |
marcozecchini | 0:9fca2b23d0ba | 129 | |
marcozecchini | 0:9fca2b23d0ba | 130 | From an architectural standpoint, event queues easily align with module |
marcozecchini | 0:9fca2b23d0ba | 131 | boundaries, where internal state can be implicitly synchronized through |
marcozecchini | 0:9fca2b23d0ba | 132 | event dispatch. |
marcozecchini | 0:9fca2b23d0ba | 133 | |
marcozecchini | 0:9fca2b23d0ba | 134 | On platforms where multiple threads are unavailable, multiple modules |
marcozecchini | 0:9fca2b23d0ba | 135 | can use independent event queues and still be composed through the |
marcozecchini | 0:9fca2b23d0ba | 136 | `equeue_chain` function. |
marcozecchini | 0:9fca2b23d0ba | 137 | |
marcozecchini | 0:9fca2b23d0ba | 138 | ``` c |
marcozecchini | 0:9fca2b23d0ba | 139 | #include "equeue.h" |
marcozecchini | 0:9fca2b23d0ba | 140 | |
marcozecchini | 0:9fca2b23d0ba | 141 | // run a simultaneous localization and mapping loop in one queue |
marcozecchini | 0:9fca2b23d0ba | 142 | struct slam { |
marcozecchini | 0:9fca2b23d0ba | 143 | equeue_t queue; |
marcozecchini | 0:9fca2b23d0ba | 144 | }; |
marcozecchini | 0:9fca2b23d0ba | 145 | |
marcozecchini | 0:9fca2b23d0ba | 146 | void slam_create(struct slam *s, equeue_t *target) { |
marcozecchini | 0:9fca2b23d0ba | 147 | equeue_create(&s->queue, 4096); |
marcozecchini | 0:9fca2b23d0ba | 148 | equeue_chain(&s->queue, target); |
marcozecchini | 0:9fca2b23d0ba | 149 | equeue_call_every(&s->queue, 100, slam_filter); |
marcozecchini | 0:9fca2b23d0ba | 150 | } |
marcozecchini | 0:9fca2b23d0ba | 151 | |
marcozecchini | 0:9fca2b23d0ba | 152 | // run a sonar with it's own queue |
marcozecchini | 0:9fca2b23d0ba | 153 | struct sonar { |
marcozecchini | 0:9fca2b23d0ba | 154 | equeue_t equeue; |
marcozecchini | 0:9fca2b23d0ba | 155 | struct slam *slam; |
marcozecchini | 0:9fca2b23d0ba | 156 | }; |
marcozecchini | 0:9fca2b23d0ba | 157 | |
marcozecchini | 0:9fca2b23d0ba | 158 | void sonar_create(struct sonar *s, equeue_t *target) { |
marcozecchini | 0:9fca2b23d0ba | 159 | equeue_create(&s->queue, 64); |
marcozecchini | 0:9fca2b23d0ba | 160 | equeue_chain(&s->queue, target); |
marcozecchini | 0:9fca2b23d0ba | 161 | equeue_call_in(&s->queue, 5, sonar_update, s); |
marcozecchini | 0:9fca2b23d0ba | 162 | } |
marcozecchini | 0:9fca2b23d0ba | 163 | |
marcozecchini | 0:9fca2b23d0ba | 164 | // all of the above queues can be combined into a single thread of execution |
marcozecchini | 0:9fca2b23d0ba | 165 | int main() { |
marcozecchini | 0:9fca2b23d0ba | 166 | equeue_t queue; |
marcozecchini | 0:9fca2b23d0ba | 167 | equeue_create(&queue, 1024); |
marcozecchini | 0:9fca2b23d0ba | 168 | |
marcozecchini | 0:9fca2b23d0ba | 169 | struct sonar s1, s2, s3; |
marcozecchini | 0:9fca2b23d0ba | 170 | sonar_create(&s1, &queue); |
marcozecchini | 0:9fca2b23d0ba | 171 | sonar_create(&s2, &queue); |
marcozecchini | 0:9fca2b23d0ba | 172 | sonar_create(&s3, &queue); |
marcozecchini | 0:9fca2b23d0ba | 173 | |
marcozecchini | 0:9fca2b23d0ba | 174 | struct slam slam; |
marcozecchini | 0:9fca2b23d0ba | 175 | slam_create(&slam, &queue); |
marcozecchini | 0:9fca2b23d0ba | 176 | |
marcozecchini | 0:9fca2b23d0ba | 177 | // dispatches events from all of the modules |
marcozecchini | 0:9fca2b23d0ba | 178 | equeue_dispatch(&queue, -1); |
marcozecchini | 0:9fca2b23d0ba | 179 | } |
marcozecchini | 0:9fca2b23d0ba | 180 | ``` |
marcozecchini | 0:9fca2b23d0ba | 181 | |
marcozecchini | 0:9fca2b23d0ba | 182 | ## Platform ## |
marcozecchini | 0:9fca2b23d0ba | 183 | |
marcozecchini | 0:9fca2b23d0ba | 184 | The equeue library has a minimal porting layer that is flexible depending |
marcozecchini | 0:9fca2b23d0ba | 185 | on the requirements of the underlying platform. Platform specific declarations |
marcozecchini | 0:9fca2b23d0ba | 186 | and more information can be found in [equeue_platform.h](equeue_platform.h). |
marcozecchini | 0:9fca2b23d0ba | 187 | |
marcozecchini | 0:9fca2b23d0ba | 188 | ## Tests ## |
marcozecchini | 0:9fca2b23d0ba | 189 | |
marcozecchini | 0:9fca2b23d0ba | 190 | The equeue library uses a set of local tests based on the posix implementation. |
marcozecchini | 0:9fca2b23d0ba | 191 | |
marcozecchini | 0:9fca2b23d0ba | 192 | Runtime tests are located in [tests.c](tests/tests.c): |
marcozecchini | 0:9fca2b23d0ba | 193 | |
marcozecchini | 0:9fca2b23d0ba | 194 | ``` bash |
marcozecchini | 0:9fca2b23d0ba | 195 | make test |
marcozecchini | 0:9fca2b23d0ba | 196 | ``` |
marcozecchini | 0:9fca2b23d0ba | 197 | |
marcozecchini | 0:9fca2b23d0ba | 198 | Profiling tests based on rdtsc are located in [prof.c](tests/prof.c): |
marcozecchini | 0:9fca2b23d0ba | 199 | |
marcozecchini | 0:9fca2b23d0ba | 200 | ``` bash |
marcozecchini | 0:9fca2b23d0ba | 201 | make prof |
marcozecchini | 0:9fca2b23d0ba | 202 | ``` |
marcozecchini | 0:9fca2b23d0ba | 203 | |
marcozecchini | 0:9fca2b23d0ba | 204 | To make profiling results more tangible, the profiler also supports percentage |
marcozecchini | 0:9fca2b23d0ba | 205 | comparison with previous runs: |
marcozecchini | 0:9fca2b23d0ba | 206 | ``` bash |
marcozecchini | 0:9fca2b23d0ba | 207 | make prof | tee results.txt |
marcozecchini | 0:9fca2b23d0ba | 208 | cat results.txt | make prof |
marcozecchini | 0:9fca2b23d0ba | 209 | ``` |
marcozecchini | 0:9fca2b23d0ba | 210 |