This repository has been archived by the owner on Nov 11, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 96
/
u_port_event_queue.h
339 lines (315 loc) · 13.7 KB
/
u_port_event_queue.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
/*
* Copyright 2019-2024 u-blox
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _U_PORT_EVENT_QUEUE_H_
#define _U_PORT_EVENT_QUEUE_H_
/* Only header files representing a direct and unavoidable
* dependency between the API of this module and the API
* of another module should be included here; otherwise
* please keep #includes to your .c files. */
/** \addtogroup __port
* @{
*/
/** @file
* @brief An event queue. Simply put, allows the user to run a
* function in its own task context, driven asynchronously, with
* parameters sent through an OS queue. These functions are
* thread-safe except that an event queue should not be closed
* while uPortEventQueueSend() or uPortEventQueueSendIrq() are
* in progress.
*
* It works like this. If you have function of the form, say:
*
* ```
* void myFunction(int32_t a, char *pBuffer)
* {
* *pBuffer = (char) a;
* )
* ```
*
* ...which you would like to run asynchronously, you would
* re-write it as:
*
* ```
* typedef struct {
* int32_t a;
* char *pBuffer;
* } myStruct_t;
*
* void myFunction(void *pParam, size_t paramLengthBytes)
* {
* myStruct_t *pMyStruct = (myStruct_t *) pParam;
*
* *(pMyStruct->pBuffer) = (char) pMyStruct->a;
* )
* ```
*
* In other words, your parameters would be defined as a
* struct and you then cast the `void *` parameter that your
* function receives to that struct before proceeding as
* normal. `paramLengthBytes` (passed through from
* `uPortEventQueueSend()`) may be useful if `pParam` is of
* variable size but it may be ignored (i.e. just add a line
*
* ```
* (void) paramLengthBytes;
* ```
*
* ...to your function to keep Lint and the compiler happy) if
* the size is fixed.
*
* `uPortEventQueueOpen()` creates the OS task in
* which `myFunction()` will run and the associated queue.
*
* A call to `uPortEventQueueSend()` with a parameter
* block will copy that parameter block onto the queue from
* where `myFunction()` will be invoked with it. This may be
* repeated as necessary. `uPortEventQueueSendIrq()` is
* a version which is safe to call from an interrupt.
*
* `uPortEventQueueClose()` shuts down the queue and deletes
* the task. This is a cooperative process: your function
* must have emptied the queue and exited for shut-down to
* complete.
*/
#ifdef __cplusplus
extern "C" {
#endif
/* ----------------------------------------------------------------
* COMPILE-TIME MACROS
* -------------------------------------------------------------- */
#ifndef U_PORT_EVENT_QUEUE_MAX_NUM
/** The maximum number of event queues.
*/
# define U_PORT_EVENT_QUEUE_MAX_NUM 20
#endif
/** The length of uEventQueueControlOrSize_t (see implementation).
*/
#define U_PORT_EVENT_QUEUE_CONTROL_OR_SIZE_LENGTH_BYTES 4
#ifndef U_PORT_EVENT_QUEUE_MAX_PARAM_LENGTH_BYTES
# if defined(U_PORT_STM32_PURE_CMSIS) && !defined(U_PORT_STM32_CMSIS_ON_FREERTOS)
/** When using ThreadX under CMSIS the maximum length of a queue item is just
* 64 bytes, hence the event queue maximum item length is similarly limited.
*/
# define U_PORT_EVENT_QUEUE_MAX_PARAM_LENGTH_BYTES ((16 * sizeof(uint32_t)) - U_PORT_EVENT_QUEUE_CONTROL_OR_SIZE_LENGTH_BYTES)
# else
# define U_PORT_EVENT_QUEUE_MAX_PARAM_LENGTH_BYTES 128
# endif
#endif
#ifndef U_PORT_EVENT_QUEUE_MIN_TASK_STACK_SIZE_BYTES
/** The minimum stack size for an event queue task; the governing
* factor here is ESP32S3, which requires ~256 bytes more stack
* than ESP32 (which is the next limit).
*/
# define U_PORT_EVENT_QUEUE_MIN_TASK_STACK_SIZE_BYTES 1024 + \
U_PORT_EVENT_QUEUE_CONTROL_OR_SIZE_LENGTH_BYTES + \
U_PORT_EVENT_QUEUE_MAX_PARAM_LENGTH_BYTES
#endif
/* ----------------------------------------------------------------
* TYPES
* -------------------------------------------------------------- */
/* ----------------------------------------------------------------
* PUBLIC FUNCTIONS
* -------------------------------------------------------------- */
/** Open an event queue.
*
* Note: some platforms place further restrictions on the maximum
* size of message that can be sent by the underlying uPortQueue API,
* and that has an impact here. For instance, ThreadX, used on
* the later STM32Cube platforms, has a limit of 64 bytes, meaning
* that paramMaxLengthBytes here, by the time
* #U_PORT_EVENT_QUEUE_CONTROL_OR_SIZE_LENGTH_BYTES is accounted
* for, is just 60 bytes.
*
* Note: in some operating systems (e.g. Zephyr) we use a
* conditional compilation flag, U_CFG_OS_MAX_THREADS, to
* limit the number of tasks that this code can create. Since an
* event queue requires both a queue and a task, should this
* function return #U_ERROR_COMMON_NO_MEMORY, you might need to
* set a bigger value for U_CFG_OS_MAX_THREADS in your build.
* If you cannot find U_CFG_OS_MAX_THREADS in the file
* u_cfg_os_platform_specific.h for your platform then this
* limitation is not relevant to you.
*
* @param[in] pFunction the function that will be called by
* the queue, cannot be NULL.
* @param[in] pName a name to give the task that is
* at the end of the event queue. May
* be NULL in which case a default name
* will be used.
* @param paramMaxLengthBytes the maximum length of the parameters
* structure to pass to the function,
* cannot be larger than
* #U_PORT_EVENT_QUEUE_MAX_PARAM_LENGTH_BYTES.
* @param stackSizeBytes the stack size of the task that the
* function will be run in, must be
* at least
* #U_PORT_EVENT_QUEUE_MIN_TASK_STACK_SIZE_BYTES.
* @param priority the priority of the task that the
* function will be run in; see
* u_cfg_os_platform_specific.h for
* your platform for more information.
* The default application, for instance,
* runs at U_CFG_OS_APP_TASK_PRIORITY. Unless
* you know what you are doing it is STRONGLY
* advised to run all your event queues at
* the same U_CFG_OS_APP_TASK_PRIORITY; this
* way the OS will round-robin schedule the
* event tasks and no-one will be starved. If
* you chose different priorities it is very
* easy for a task to become starved of run-time,
* meaning it will not be able to empty its queue,
* the queue may become full and then
* uPortEventQueueSend() will block. This holds
* true even for an event queue being fed from
* an interrupt if the receiving task is
* forwarding the events to another queue: all
* the event tasks should run at the same
* priority otherwise the effective queue depth
* is that of one link in the chain, not the sum
* of the links in the chain, and you risk dropping
* characters at uPortEventQueueSendIrq().
* @param queueLength the number of items to let onto the
* queue before blocking or returning an
* error, must be at least 1.
* @return a handle for the event queue on success,
* else negative error code.
*/
int32_t uPortEventQueueOpen(void (*pFunction) (void *, size_t),
const char *pName,
size_t paramMaxLengthBytes,
size_t stackSizeBytes,
int32_t priority,
size_t queueLength);
/** Send to an event queue. The data at pParam will be copied
* onto the queue. If the queue is full this function will block
* until room is available. An event queue should not be closed
* while this function is in progress.
*
* @param handle the handle for the event queue.
* @param[in] pParam a pointer to the parameters structure
* to send. May be NULL, in which case
* paramLengthBytes must be zero.
* @param paramLengthBytes the length of the parameters
* structure. Must be less than or
* equal to paramMaxLengthBytes as
* given to uPortEventQueueOpen().
* @return zero on success else negative error code.
*/
int32_t uPortEventQueueSend(int32_t handle, const void *pParam,
size_t paramLengthBytes);
/** Send to an event queue from an interrupt. The data at
* pParam will be copied onto the queue. If the queue is full
* the event will not be sent and an error will be returned.
* Note: you must ensure that your interrupt stack is large
* enough to hold an array of size paramLengthBytes +
* #U_PORT_EVENT_QUEUE_CONTROL_OR_SIZE_LENGTH_BYTES. An event
* queue should not be closed while this function is in
* progress.
*
* @param handle the handle for the event queue.
* @param[in] pParam a pointer to the parameters structure
* to send. May be NULL, in which case
* paramLengthBytes must be zero.
* @param paramLengthBytes the length of the parameters
* structure. Must be less than or
* equal to paramMaxLengthBytes as
* given to uPortEventQueueOpen().
* @return zero on success else negative error code.
*/
int32_t uPortEventQueueSendIrq(int32_t handle, const void *pParam,
size_t paramLengthBytes);
/** Detect whether the task currently executing is the
* event task for the given event queue. Useful if you
* have code which is called a few levels down from the
* event handler both by event code and other code and
* needs to know which context it is in.
*
* @param handle the handle for the event queue.
* @return true if the current task is the event
* task for the given handle, else false.
*/
bool uPortEventQueueIsTask(int32_t handle);
/** Get the stack high watermark, the minimum free
* stack, for the task at the end of the given event
* queue in bytes.
*
* @param handle the handle of the queue to check.
* @return the minimum stack free for the lifetime
* of the event task in bytes, else
* negative error code.
*/
int32_t uPortEventQueueStackMinFree(int32_t handle);
/** Close an event queue.
*
* Note: this function does not free memory, that is done
* on de-initialisation; if you want to free memory before
* then, call uPortEventQueueCleanUp().
*
* COMMON CODING ERROR: there is a common coding error
* in the use of this function which can lead to a mutex
* deadlock. It goes as follows:
*
* - an event queue is used by an API, and that API
* protects all of its functions for re-entrancy with
* a mutex M,
* - the event callback function passed to
* uPortEventQueueOpen(), let's call it C(), also locks
mutex M,
* - when the API is closed, the function that closes the API
* locks mutex M and then calls uPortEventQueueClose(),
* - in order to exit, the event queue code has to shut-down
* the task that it launched to run the call-back in, and
* such an event, as is the nature of events, can happen
* at any time...
* - so, after the function that closes the API has locked
* mutex M, such an even goes off; C() is now going to be
* called but it can't have mutex M 'cos it has already
* been locked, it sits there waiting for the mutex,
* - the function that closes the API goes on to call
* uPortEventQueueClose(), which cannot complete because
* the task it is running to call C() cannot exit.
*
* We're mutex locked.
*
* To avoid this pitfall you MUST MAKE SURE that C()
* either (a) does not lock your API mutex or, if it does,
* you let it know when a shut-down is in progress so that
* it can ignore any events during that time and not try
* to lock the mutex at all.
*
* @param handle the handle of the event queue to close.
* @return zero on success else negative error code.
*/
int32_t uPortEventQueueClose(int32_t handle);
/** Get the number of entries free on the given event queue.
* It is NOT a requirement that this API is implemented:
* where it is not implemented #U_ERROR_COMMON_NOT_IMPLEMENTED
* should be returned.
*
* @param handle the handle of the event queue.
* @return on success the number of entries free, else
* negative error code.
*/
int32_t uPortEventQueueGetFree(int32_t handle);
/** Free memory occupied by closed event queues.
*/
void uPortEventQueueCleanUp(void);
#ifdef __cplusplus
}
#endif
/** @}*/
#endif // _U_PORT_EVENT_QUEUE_H_
// End of file