User | Revision | Line number | New contents of line |
switches |
0:0e018d759a2a
|
1
|
|
switches |
0:0e018d759a2a
|
2
|
/** \addtogroup events */
|
switches |
0:0e018d759a2a
|
3
|
/** @{*/
|
switches |
0:0e018d759a2a
|
4
|
/*
|
switches |
0:0e018d759a2a
|
5
|
* Flexible event queue for dispatching events
|
switches |
0:0e018d759a2a
|
6
|
*
|
switches |
0:0e018d759a2a
|
7
|
* Copyright (c) 2016 Christopher Haster
|
switches |
0:0e018d759a2a
|
8
|
*
|
switches |
0:0e018d759a2a
|
9
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
switches |
0:0e018d759a2a
|
10
|
* you may not use this file except in compliance with the License.
|
switches |
0:0e018d759a2a
|
11
|
* You may obtain a copy of the License at
|
switches |
0:0e018d759a2a
|
12
|
*
|
switches |
0:0e018d759a2a
|
13
|
* http://www.apache.org/licenses/LICENSE-2.0
|
switches |
0:0e018d759a2a
|
14
|
*
|
switches |
0:0e018d759a2a
|
15
|
* Unless required by applicable law or agreed to in writing, software
|
switches |
0:0e018d759a2a
|
16
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
switches |
0:0e018d759a2a
|
17
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
switches |
0:0e018d759a2a
|
18
|
* See the License for the specific language governing permissions and
|
switches |
0:0e018d759a2a
|
19
|
* limitations under the License.
|
switches |
0:0e018d759a2a
|
20
|
*/
|
switches |
0:0e018d759a2a
|
21
|
#ifndef EQUEUE_H
|
switches |
0:0e018d759a2a
|
22
|
#define EQUEUE_H
|
switches |
0:0e018d759a2a
|
23
|
|
switches |
0:0e018d759a2a
|
24
|
#ifdef __cplusplus
|
switches |
0:0e018d759a2a
|
25
|
extern "C" {
|
switches |
0:0e018d759a2a
|
26
|
#endif
|
switches |
0:0e018d759a2a
|
27
|
|
switches |
0:0e018d759a2a
|
28
|
// Platform specific files
|
switches |
0:0e018d759a2a
|
29
|
#include "equeue/equeue_platform.h"
|
switches |
0:0e018d759a2a
|
30
|
|
switches |
0:0e018d759a2a
|
31
|
#include <stddef.h>
|
switches |
0:0e018d759a2a
|
32
|
#include <stdint.h>
|
switches |
0:0e018d759a2a
|
33
|
|
switches |
0:0e018d759a2a
|
34
|
|
switches |
0:0e018d759a2a
|
35
|
// The minimum size of an event
|
switches |
0:0e018d759a2a
|
36
|
// This size is guaranteed to fit events created by event_call
|
switches |
0:0e018d759a2a
|
37
|
#define EQUEUE_EVENT_SIZE (sizeof(struct equeue_event) + 2*sizeof(void*))
|
switches |
0:0e018d759a2a
|
38
|
|
switches |
0:0e018d759a2a
|
39
|
// Internal event structure
|
switches |
0:0e018d759a2a
|
40
|
struct equeue_event {
|
switches |
0:0e018d759a2a
|
41
|
unsigned size;
|
switches |
0:0e018d759a2a
|
42
|
uint8_t id;
|
switches |
0:0e018d759a2a
|
43
|
uint8_t generation;
|
switches |
0:0e018d759a2a
|
44
|
|
switches |
0:0e018d759a2a
|
45
|
struct equeue_event *next;
|
switches |
0:0e018d759a2a
|
46
|
struct equeue_event *sibling;
|
switches |
0:0e018d759a2a
|
47
|
struct equeue_event **ref;
|
switches |
0:0e018d759a2a
|
48
|
|
switches |
0:0e018d759a2a
|
49
|
unsigned target;
|
switches |
0:0e018d759a2a
|
50
|
int period;
|
switches |
0:0e018d759a2a
|
51
|
void (*dtor)(void *);
|
switches |
0:0e018d759a2a
|
52
|
|
switches |
0:0e018d759a2a
|
53
|
void (*cb)(void *);
|
switches |
0:0e018d759a2a
|
54
|
// data follows
|
switches |
0:0e018d759a2a
|
55
|
};
|
switches |
0:0e018d759a2a
|
56
|
|
switches |
0:0e018d759a2a
|
57
|
// Event queue structure
|
switches |
0:0e018d759a2a
|
58
|
typedef struct equeue {
|
switches |
0:0e018d759a2a
|
59
|
struct equeue_event *queue;
|
switches |
0:0e018d759a2a
|
60
|
unsigned tick;
|
switches |
0:0e018d759a2a
|
61
|
unsigned breaks;
|
switches |
0:0e018d759a2a
|
62
|
uint8_t generation;
|
switches |
0:0e018d759a2a
|
63
|
|
switches |
0:0e018d759a2a
|
64
|
unsigned char *buffer;
|
switches |
0:0e018d759a2a
|
65
|
unsigned npw2;
|
switches |
0:0e018d759a2a
|
66
|
void *allocated;
|
switches |
0:0e018d759a2a
|
67
|
|
switches |
0:0e018d759a2a
|
68
|
struct equeue_event *chunks;
|
switches |
0:0e018d759a2a
|
69
|
struct equeue_slab {
|
switches |
0:0e018d759a2a
|
70
|
size_t size;
|
switches |
0:0e018d759a2a
|
71
|
unsigned char *data;
|
switches |
0:0e018d759a2a
|
72
|
} slab;
|
switches |
0:0e018d759a2a
|
73
|
|
switches |
0:0e018d759a2a
|
74
|
struct equeue_background {
|
switches |
0:0e018d759a2a
|
75
|
bool active;
|
switches |
0:0e018d759a2a
|
76
|
void (*update)(void *timer, int ms);
|
switches |
0:0e018d759a2a
|
77
|
void *timer;
|
switches |
0:0e018d759a2a
|
78
|
} background;
|
switches |
0:0e018d759a2a
|
79
|
|
switches |
0:0e018d759a2a
|
80
|
equeue_sema_t eventsema;
|
switches |
0:0e018d759a2a
|
81
|
equeue_mutex_t queuelock;
|
switches |
0:0e018d759a2a
|
82
|
equeue_mutex_t memlock;
|
switches |
0:0e018d759a2a
|
83
|
} equeue_t;
|
switches |
0:0e018d759a2a
|
84
|
|
switches |
0:0e018d759a2a
|
85
|
|
switches |
0:0e018d759a2a
|
86
|
// Queue lifetime operations
|
switches |
0:0e018d759a2a
|
87
|
//
|
switches |
0:0e018d759a2a
|
88
|
// Creates and destroys an event queue. The event queue either allocates a
|
switches |
0:0e018d759a2a
|
89
|
// buffer of the specified size with malloc or uses a user provided buffer
|
switches |
0:0e018d759a2a
|
90
|
// if constructed with equeue_create_inplace.
|
switches |
0:0e018d759a2a
|
91
|
//
|
switches |
0:0e018d759a2a
|
92
|
// If the event queue creation fails, equeue_create returns a negative,
|
switches |
0:0e018d759a2a
|
93
|
// platform-specific error code.
|
switches |
0:0e018d759a2a
|
94
|
int equeue_create(equeue_t *queue, size_t size);
|
switches |
0:0e018d759a2a
|
95
|
int equeue_create_inplace(equeue_t *queue, size_t size, void *buffer);
|
switches |
0:0e018d759a2a
|
96
|
void equeue_destroy(equeue_t *queue);
|
switches |
0:0e018d759a2a
|
97
|
|
switches |
0:0e018d759a2a
|
98
|
// Dispatch events
|
switches |
0:0e018d759a2a
|
99
|
//
|
switches |
0:0e018d759a2a
|
100
|
// Executes events until the specified milliseconds have passed. If ms is
|
switches |
0:0e018d759a2a
|
101
|
// negative, equeue_dispatch will dispatch events indefinitely or until
|
switches |
0:0e018d759a2a
|
102
|
// equeue_break is called on this queue.
|
switches |
0:0e018d759a2a
|
103
|
//
|
switches |
0:0e018d759a2a
|
104
|
// When called with a finite timeout, the equeue_dispatch function is
|
switches |
0:0e018d759a2a
|
105
|
// guaranteed to terminate. When called with a timeout of 0, the
|
switches |
0:0e018d759a2a
|
106
|
// equeue_dispatch does not wait and is irq safe.
|
switches |
0:0e018d759a2a
|
107
|
void equeue_dispatch(equeue_t *queue, int ms);
|
switches |
0:0e018d759a2a
|
108
|
|
switches |
0:0e018d759a2a
|
109
|
// Break out of a running event loop
|
switches |
0:0e018d759a2a
|
110
|
//
|
switches |
0:0e018d759a2a
|
111
|
// Forces the specified event queue's dispatch loop to terminate. Pending
|
switches |
0:0e018d759a2a
|
112
|
// events may finish executing, but no new events will be executed.
|
switches |
0:0e018d759a2a
|
113
|
void equeue_break(equeue_t *queue);
|
switches |
0:0e018d759a2a
|
114
|
|
switches |
0:0e018d759a2a
|
115
|
// Simple event calls
|
switches |
0:0e018d759a2a
|
116
|
//
|
switches |
0:0e018d759a2a
|
117
|
// The specified callback will be executed in the context of the event queue's
|
switches |
0:0e018d759a2a
|
118
|
// dispatch loop. When the callback is executed depends on the call function.
|
switches |
0:0e018d759a2a
|
119
|
//
|
switches |
0:0e018d759a2a
|
120
|
// equeue_call - Immediately post an event to the queue
|
switches |
0:0e018d759a2a
|
121
|
// equeue_call_in - Post an event after a specified time in milliseconds
|
switches |
0:0e018d759a2a
|
122
|
// equeue_call_every - Post an event periodically every milliseconds
|
switches |
0:0e018d759a2a
|
123
|
//
|
switches |
0:0e018d759a2a
|
124
|
// All equeue_call functions are irq safe and can act as a mechanism for
|
switches |
0:0e018d759a2a
|
125
|
// moving events out of irq contexts.
|
switches |
0:0e018d759a2a
|
126
|
//
|
switches |
0:0e018d759a2a
|
127
|
// The return value is a unique id that represents the posted event and can
|
switches |
0:0e018d759a2a
|
128
|
// be passed to equeue_cancel. If there is not enough memory to allocate the
|
switches |
0:0e018d759a2a
|
129
|
// event, equeue_call returns an id of 0.
|
switches |
0:0e018d759a2a
|
130
|
int equeue_call(equeue_t *queue, void (*cb)(void *), void *data);
|
switches |
0:0e018d759a2a
|
131
|
int equeue_call_in(equeue_t *queue, int ms, void (*cb)(void *), void *data);
|
switches |
0:0e018d759a2a
|
132
|
int equeue_call_every(equeue_t *queue, int ms, void (*cb)(void *), void *data);
|
switches |
0:0e018d759a2a
|
133
|
|
switches |
0:0e018d759a2a
|
134
|
// Allocate memory for events
|
switches |
0:0e018d759a2a
|
135
|
//
|
switches |
0:0e018d759a2a
|
136
|
// The equeue_alloc function allocates an event that can be manually dispatched
|
switches |
0:0e018d759a2a
|
137
|
// with equeue_post. The equeue_dealloc function may be used to free an event
|
switches |
0:0e018d759a2a
|
138
|
// that has not been posted. Once posted, an event's memory is managed by the
|
switches |
0:0e018d759a2a
|
139
|
// event queue and should not be deallocated.
|
switches |
0:0e018d759a2a
|
140
|
//
|
switches |
0:0e018d759a2a
|
141
|
// Both equeue_alloc and equeue_dealloc are irq safe.
|
switches |
0:0e018d759a2a
|
142
|
//
|
switches |
0:0e018d759a2a
|
143
|
// The equeue allocator is designed to minimize jitter in interrupt contexts as
|
switches |
0:0e018d759a2a
|
144
|
// well as avoid memory fragmentation on small devices. The allocator achieves
|
switches |
0:0e018d759a2a
|
145
|
// both constant-runtime and zero-fragmentation for fixed-size events, however
|
switches |
0:0e018d759a2a
|
146
|
// grows linearly as the quantity of different sized allocations increases.
|
switches |
0:0e018d759a2a
|
147
|
//
|
switches |
0:0e018d759a2a
|
148
|
// The equeue_alloc function returns a pointer to the event's allocated memory
|
switches |
0:0e018d759a2a
|
149
|
// and acts as a handle to the underlying event. If there is not enough memory
|
switches |
0:0e018d759a2a
|
150
|
// to allocate the event, equeue_alloc returns null.
|
switches |
0:0e018d759a2a
|
151
|
void *equeue_alloc(equeue_t *queue, size_t size);
|
switches |
0:0e018d759a2a
|
152
|
void equeue_dealloc(equeue_t *queue, void *event);
|
switches |
0:0e018d759a2a
|
153
|
|
switches |
0:0e018d759a2a
|
154
|
// Configure an allocated event
|
switches |
0:0e018d759a2a
|
155
|
//
|
switches |
0:0e018d759a2a
|
156
|
// equeue_event_delay - Millisecond delay before dispatching an event
|
switches |
0:0e018d759a2a
|
157
|
// equeue_event_period - Millisecond period for repeating dispatching an event
|
switches |
0:0e018d759a2a
|
158
|
// equeue_event_dtor - Destructor to run when the event is deallocated
|
switches |
0:0e018d759a2a
|
159
|
void equeue_event_delay(void *event, int ms);
|
switches |
0:0e018d759a2a
|
160
|
void equeue_event_period(void *event, int ms);
|
switches |
0:0e018d759a2a
|
161
|
void equeue_event_dtor(void *event, void (*dtor)(void *));
|
switches |
0:0e018d759a2a
|
162
|
|
switches |
0:0e018d759a2a
|
163
|
// Post an event onto the event queue
|
switches |
0:0e018d759a2a
|
164
|
//
|
switches |
0:0e018d759a2a
|
165
|
// The equeue_post function takes a callback and a pointer to an event
|
switches |
0:0e018d759a2a
|
166
|
// allocated by equeue_alloc. The specified callback will be executed in the
|
switches |
0:0e018d759a2a
|
167
|
// context of the event queue's dispatch loop with the allocated event
|
switches |
0:0e018d759a2a
|
168
|
// as its argument.
|
switches |
0:0e018d759a2a
|
169
|
//
|
switches |
0:0e018d759a2a
|
170
|
// The equeue_post function is irq safe and can act as a mechanism for
|
switches |
0:0e018d759a2a
|
171
|
// moving events out of irq contexts.
|
switches |
0:0e018d759a2a
|
172
|
//
|
switches |
0:0e018d759a2a
|
173
|
// The return value is a unique id that represents the posted event and can
|
switches |
0:0e018d759a2a
|
174
|
// be passed to equeue_cancel.
|
switches |
0:0e018d759a2a
|
175
|
int equeue_post(equeue_t *queue, void (*cb)(void *), void *event);
|
switches |
0:0e018d759a2a
|
176
|
|
switches |
0:0e018d759a2a
|
177
|
// Cancel an in-flight event
|
switches |
0:0e018d759a2a
|
178
|
//
|
switches |
0:0e018d759a2a
|
179
|
// Attempts to cancel an event referenced by the unique id returned from
|
switches |
0:0e018d759a2a
|
180
|
// equeue_call or equeue_post. It is safe to call equeue_cancel after an event
|
switches |
0:0e018d759a2a
|
181
|
// has already been dispatched.
|
switches |
0:0e018d759a2a
|
182
|
//
|
switches |
0:0e018d759a2a
|
183
|
// The equeue_cancel function is irq safe.
|
switches |
0:0e018d759a2a
|
184
|
//
|
switches |
0:0e018d759a2a
|
185
|
// If called while the event queue's dispatch loop is active, equeue_cancel
|
switches |
0:0e018d759a2a
|
186
|
// does not guarantee that the event will not not execute after it returns as
|
switches |
0:0e018d759a2a
|
187
|
// the event may have already begun executing.
|
switches |
0:0e018d759a2a
|
188
|
void equeue_cancel(equeue_t *queue, int id);
|
switches |
0:0e018d759a2a
|
189
|
|
switches |
0:0e018d759a2a
|
190
|
// Background an event queue onto a single-shot timer
|
switches |
0:0e018d759a2a
|
191
|
//
|
switches |
0:0e018d759a2a
|
192
|
// The provided update function will be called to indicate when the queue
|
switches |
0:0e018d759a2a
|
193
|
// should be dispatched. A negative timeout will be passed to the update
|
switches |
0:0e018d759a2a
|
194
|
// function when the timer is no longer needed.
|
switches |
0:0e018d759a2a
|
195
|
//
|
switches |
0:0e018d759a2a
|
196
|
// Passing a null update function disables the existing timer.
|
switches |
0:0e018d759a2a
|
197
|
//
|
switches |
0:0e018d759a2a
|
198
|
// The equeue_background function allows an event queue to take advantage
|
switches |
0:0e018d759a2a
|
199
|
// of hardware timers or even other event loops, allowing an event queue to
|
switches |
0:0e018d759a2a
|
200
|
// be effectively backgrounded.
|
switches |
0:0e018d759a2a
|
201
|
void equeue_background(equeue_t *queue,
|
switches |
0:0e018d759a2a
|
202
|
void (*update)(void *timer, int ms), void *timer);
|
switches |
0:0e018d759a2a
|
203
|
|
switches |
0:0e018d759a2a
|
204
|
// Chain an event queue onto another event queue
|
switches |
0:0e018d759a2a
|
205
|
//
|
switches |
0:0e018d759a2a
|
206
|
// After chaining a queue to a target, calling equeue_dispatch on the
|
switches |
0:0e018d759a2a
|
207
|
// target queue will also dispatch events from this queue. The queues
|
switches |
0:0e018d759a2a
|
208
|
// use their own buffers and events must be managed independently.
|
switches |
0:0e018d759a2a
|
209
|
//
|
switches |
0:0e018d759a2a
|
210
|
// Passing a null queue as the target will unchain the existing queue.
|
switches |
0:0e018d759a2a
|
211
|
//
|
switches |
0:0e018d759a2a
|
212
|
// The equeue_chain function allows multiple equeues to be composed, sharing
|
switches |
0:0e018d759a2a
|
213
|
// the context of a dispatch loop while still being managed independently.
|
switches |
0:0e018d759a2a
|
214
|
void equeue_chain(equeue_t *queue, equeue_t *target);
|
switches |
0:0e018d759a2a
|
215
|
|
switches |
0:0e018d759a2a
|
216
|
|
switches |
0:0e018d759a2a
|
217
|
#ifdef __cplusplus
|
switches |
0:0e018d759a2a
|
218
|
}
|
switches |
0:0e018d759a2a
|
219
|
#endif
|
switches |
0:0e018d759a2a
|
220
|
|
switches |
0:0e018d759a2a
|
221
|
#endif
|
switches |
0:0e018d759a2a
|
222
|
|
switches |
0:0e018d759a2a
|
223
|
/** @}*/
|