EM-ODP 4.4.0
Event Machine on ODP
Loading...
Searching...
No Matches
event_machine_event_group.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2015-2026, Nokia Solutions and Networks
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of the copyright holder nor the names of its
15 * contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#ifndef EVENT_MACHINE_EVENT_GROUP_H_
32#define EVENT_MACHINE_EVENT_GROUP_H_
33
34#pragma GCC visibility push(default)
35
36/**
37 * @file
38 * @defgroup em_event_group Event group
39 * Event Machine fork-join helper.
40 * @{
41 *
42 * An event group can be used to trigger a join of parallel operations in the
43 * form of notification events. The number of parallel operations needs to be
44 * known in advance by the event group creator, but the separate event handlers
45 * don't necessarily need to know anything about the other related events.
46 * An event group is functionally a shared atomic counter decremented when each
47 * related event has been handled (EO-receive() returns). The notification
48 * events are automatically sent once the count reaches zero.
49 *
50 * There are two separate main usage patterns:
51 *
52 * Sender originated (original):
53 * ----------------------------
54 * 1. an event group is allocated with em_event_group_create().
55 *
56 * 2. the number of parallel events and the notifications are set with
57 * em_event_group_apply().
58 *
59 * 3. the (parallel) events are sent normally but using em_send_group() instead
60 * of em_send(). This tags the event with the given event group.
61 *
62 * 4. once received by a core the tag is used to switch core specific current
63 * event group to the one in the tag. The receiver EO handles the event
64 * normally (does not see any difference).
65 *
66 * 5. as the receive function returns the count of the current event group is
67 * decremented. If the count reaches zero (last event) the related
68 * notification event(s) are sent automatically and can trigger the next
69 * operation for the application.
70 *
71 * 6. the sequence can continue from step 2 for a new set of events if the
72 * event group is to be reused.
73 *
74 * Receiver originated (API 1.2):
75 * -----------------------------
76 * 1. an event group is created with em_event_group_create().
77 *
78 * 2. the number of parallel events and the notifications are set with
79 * em_event_group_apply().
80 *
81 * 3. during the processing of any received event that is not already tagged to
82 * belong to an event group, em_event_group_assign() can be used to set the
83 * current event group (a core local value). The rest is then equivalent to
84 * as if the event was originally sent to an event group.
85 *
86 * 4. as the receive function returns the count of the current event group is
87 * decremented. If the count reaches zero (last event) the related
88 * notification event(s) are sent automatically and can trigger the next
89 * operation for the application.
90 *
91 * 5. the sequence can continue from step 2 for a new set of events if the
92 * event group is to be reused.
93 *
94 * From an application (EO) point of view, an event group can get activated
95 * either by entering the EO receive with an event tagged to an event group or
96 * by explicitly calling em_event_group_assign. The current event group is core
97 * local and only one event group can be active (current) at a time.
98 * Assigning a received event that already is tagged to an event group, e.g.
99 * sent with em_send_group(), is not allowed unless the event group is
100 * deactivated first with em_event_group_processing_end().
101 * The current event group gets deactivated by exiting the EO receive function
102 * or by explicitly calling em_event_group_processing_end(). Deactivation means
103 * the count of the event group is decremented and if the count reaches zero
104 * the notification events are sent.
105 * The current event group is local to a core (dispatcher) and exists only
106 * within the EO receive function.
107 *
108 * Note, that event groups may only work with events that are to be handled by
109 * an EO, i.e. SW events.
110 *
111 * The EM implementation should internally use a generation count or other
112 * technique to make sure that em_event_group_abort() can stop a problem
113 * propagation, i.e. after a group is aborted (and applied a new count) any
114 * potential delayed event(s) from the previous cycle will not cause the new
115 * count to be decremented.
116 * The same should be valid for excess group events, i.e. when sending more
117 * than the applied count.
118 * To make it possible for the application to properly handle such problems,
119 * the implementation should pre-check incoming events and call error handler
120 * before giving the event to an EO. This makes it possible for the application
121 * to choose whether to drop those events (at the error handler) or let them be
122 * processed.
123 *
124 * It is not allowed to use event references with event groups since assigning
125 * an event that has references to an event group would assign all the
126 * references to the event group resulting in undefined behaviour. E.g. using
127 * em_send_group()/em_send_group_multi() to send a reference is wrong.
128 */
129
132
133#ifdef __cplusplus
134extern "C" {
135#endif
136
137/**
138 * Create a new event group for fork-join.
139 *
140 * The amount of simultaneous event groups can be limited.
141 *
142 * @return The new event group or EM_EVENT_GROUP_UNDEF if no event group is
143 * available.
144 *
145 * @see em_event_group_delete(), em_event_group_apply()
146 */
147em_event_group_t em_event_group_create(void);
148
149/**
150 * Delete (unallocate) an event group.
151 *
152 * An event group must not be deleted before it has been completed
153 * (count reached zero) or aborted. A created but never applied event group
154 * can be deleted.
155 *
156 * @param event_group Event group to delete
157 *
158 * @return EM_OK if successful.
159 *
160 * @see em_event_group_create(), em_event_group_abort()
161 */
162em_status_t em_event_group_delete(em_event_group_t event_group);
163
164/**
165 * Apply event group configuration.
166 *
167 * This function sets the event count and notification parameters for the event
168 * group. After it returns, events sent or assigned to the event group are
169 * counted against the current count value. Notification events are sent when
170 * all (counted) events have been processed (count is decremented at EO receive
171 * return or by calling em_event_group_processing_end()). A new apply call is
172 * needed to reuse the event group for another cycle (with a new count and
173 * notifications).
174 *
175 * Notification events can optionally be sent to/tagged with another event
176 * group but not with the same event group that triggered the notifications,
177 * see em_notif_t for more.
178 *
179 * @attention em_event_group_apply() can only be used on a newly created event
180 * group or when the previous cycle is completed or successfully aborted.
181 * Application can use em_event_group_is_ready() to detect whether apply is
182 * allowed but would normally use a notification to setup a new cycle
183 * (implementation must make sure that when any of the notifications is
184 * received the group is ready for new apply).
185 *
186 * Apply should only be called once per group cycle.
187 *
188 * @param event_group Event group
189 * @param count Number of events in the group (positive integer)
190 * @param num_notif Number of notification events to send
191 * @param notif_tbl Table of notifications (events and target queues)
192 *
193 * @return EM_OK if successful.
194 *
195 * @see em_event_group_create(), em_send_group(), em_event_group_is_ready(),
196 * em_notif_t
197 */
198em_status_t em_event_group_apply(em_event_group_t event_group, int count,
199 int num_notif, const em_notif_t notif_tbl[]);
200
201/**
202 * Increment the current event group count.
203 *
204 * Increments the event count of the currently active event group (received or
205 * assigned event). Enables sending new events into the current event group.
206 * The event count cannot be decremented and this will fail if there is no
207 * current event group.
208 *
209 * @param count Number of events to add to the event group (positive integer)
210 *
211 * @return EM_OK if successful.
212 *
213 * @see em_send_group(), em_event_group_apply()
214 */
216
217/**
218 * Checks if the event group is ready for 'apply'.
219 *
220 * Returns EM_TRUE (1) if the given event group is ready, i.e. the user can do
221 * em_event_group_apply() again. A better alternative to this is to use a
222 * related notification event to reuse the event group (apply can always be
223 * used when handling a notification event from the event group).
224 *
225 * An event group that has been applied a count but no events sent is not
226 * considered 'ready for apply'. If a change is needed the group has to be
227 * aborted and then re-applied.
228 *
229 * Return value EM_TRUE does not guarantee all notifications are received nor
230 * handled, but the event group count has reached zero and the event group
231 * is ready for a new apply.
232 *
233 * @param event_group Event group
234 *
235 * @return EM_TRUE if the given event group is ready for apply
236 *
237 * @see em_event_group_create(), em_event_group_apply()
238 */
239int em_event_group_is_ready(em_event_group_t event_group);
240
241/**
242 * Return the currently active event group.
243 *
244 * Returns the current event group or EM_EVENT_GROUP_UNDEF if an event group is
245 * not active (i.e. never activated or deactivated using
246 * em_event_group_processing_end()).
247 *
248 * Can only be used within an EO receive function.
249 *
250 * @return Current event group or EM_EVENT_GROUP_UNDEF
251 *
252 * @see em_event_group_create()
253 */
254em_event_group_t em_event_group_current(void);
255
256/**
257 * Send event associated with/tagged to an event group.
258 *
259 * Any valid event and destination queue parameters can be used. The event
260 * group indicates which event group the event is tagged to. The event group
261 * has to first be created and applied a count.
262 * One should always send the correct amount of events to an event group, i.e.
263 * matching the applied count.
264 *
265 * Sending with an event group to an unscheduled queue is not supported. The
266 * event group count is decremented only when an EO receive function returns;
267 * events on an unscheduled queue are dequeued directly by the application and
268 * no decrement occurs, so the count would never reach zero.
269 * Sending with EM_EVENT_GROUP_UNDEF to an unscheduled queue is allowed (then
270 * equivalent to em_send()).
271 *
272 * It is not allowed to use event references with event groups since assigning
273 * an event that has references to an event group would assign all the
274 * references to the event group resulting in undefined behaviour. E.g. using
275 * em_send_group() to send a reference is wrong.
276 *
277 * @param event Event to send
278 * @param queue Destination queue
279 * @param event_group Event group
280 *
281 * @return EM_OK if successful.
282 *
283 * @see em_send(), em_event_group_create(), em_event_group_apply(),
284 * em_event_group_increment()
285 */
286em_status_t em_send_group(em_event_t event, em_queue_t queue,
287 em_event_group_t event_group);
288
289/**
290 * Send multiple events associated with/tagged to an event group.
291 *
292 * This is like em_send_group, but multiple events can be sent with one call
293 * for potential performance gain.
294 * The call returns the number of events actually sent. A return value equal to
295 * 'num' means that all events were sent. A value less than 'num' means the
296 * events at the end of the given event list were not sent and must be handled
297 * by the application.
298 * The function will not modify the given list of events.
299 *
300 * Sending with an event group to an unscheduled queue is not supported. The
301 * event group count is decremented only when an EO receive function returns;
302 * events on an unscheduled queue are dequeued directly by the application and
303 * no decrement occurs, so the count would never reach zero.
304 * Sending with EM_EVENT_GROUP_UNDEF to an unscheduled queue is allowed (then
305 * equivalent to em_send_multi()).
306 *
307 * It is not allowed to use event references with event groups since assigning
308 * an event that has references to an event group would assign all the
309 * references to the event group resulting in undefined behaviour. E.g. using
310 * em_send_group_multi() to send references is wrong.
311 *
312 * @param events List of events to send (i.e. ptr to array of events)
313 * @param num Number of events
314 * @param queue Destination queue
315 * @param event_group Event group
316 *
317 * @return number of events successfully sent (equal to num if all successful)
318 *
319 * @see em_send_group()
320 */
321int em_send_group_multi(const em_event_t events[], int num, em_queue_t queue,
322 em_event_group_t event_group);
323
324/**
325 * Signal early end of processing of the current event group
326 *
327 * This is an optional call that can be used to move the implicit event group
328 * handling (decrementing the count) from exiting event receive function to the
329 * point of this call - the current event group count is decremented
330 * immediately and if it reaches zero the notifications are also sent. In that
331 * case the group will be ready for a new apply after this returns.
332 *
333 * This impacts the current event group the same way whether it was activated
334 * by receiving a tagged event or EO called em_event_group_assign().
335 *
336 * This call does not change potential atomicity or ordering for the current
337 * event and is a no-operation if called while an event group is not active
338 * (no current group).
339 *
340 * Can only be used within the EO receive function.
341 */
343
344/**
345 * Assign core local current event group.
346 *
347 * The assign functionality can be used to set the core local current event
348 * group. The event group handling after the assign call is identical to
349 * the handling of an event group that was originally set by sending an event
350 * tagged to that event group, i.e. the core local current event group
351 * is active and will be operated on in a normal way.
352 * Assign will fail if there already is an active current event group, i.e.
353 * only one event group can be active at a time (per core).
354 *
355 * This needs to be used with care, i.e. match the amount of events applied
356 * and assigned.
357 *
358 * @param event_group An applied event group to assign to
359 *
360 * @return EM_OK if assignment was successful
361 */
362em_status_t em_event_group_assign(em_event_group_t event_group);
363
364/**
365 * Abort the ongoing event group.
366 *
367 * This is a recovery operation to abort an ongoing event group in case it does
368 * not get completed. This will reset the group back to a state ready for
369 * a new apply. Note, that there is a potential race as the group could get
370 * completed on another thread while executing this (e.g. a delayed event is
371 * finally received and processed). Implementation will synchronize internal
372 * state changes, but this call may succeed or fail depending on timing so
373 * abort should be done with care for recovery purpose only.
374 *
375 * Notification events related to the ongoing (to be aborted) cycle can be
376 * managed as follows
377 * 1) save possible related notifications using em_event_group_notif()
378 * 2) call em_event_group_abort()
379 * 3) IF em_event_group_abort() returns EM_OK the operation was successfully
380 * completed meaning the earlier notifications will not be sent thus the
381 * saved notifications can be freed or reused. Otherwise the call was made
382 * too late and the saved notifications must not be touched as they are to
383 * be sent.
384 *
385 * This means the synchronization point is em_event_group_abort(), not
386 * em_event_group_notif() which might return notifications that will still
387 * be sent.
388 *
389 * @attention Related notification events will not be automatically freed in
390 * any case and must be handled by the application.
391 *
392 * @param event_group Event group to abort and reset
393 *
394 * @return EM_OK if the call was made early enough to cleanly abort, i.e.
395 * before the last event was processed. EM_OK also means the
396 * notifications will not be sent.
397 */
398em_status_t em_event_group_abort(em_event_group_t event_group);
399
400/**
401 * Return notification events currently related to an applied event group.
402 *
403 * This returns the current notifications or none (0) if they were already sent
404 * (event group completed).
405 *
406 * @attention This is not a synchronization point, which means
407 * em_event_group_notif() could return notifications which
408 * are just going to be sent and thus should not be touched.
409 *
410 * @param event_group Event group
411 * @param max_notif Maximum number of notifications to return
412 * @param[out] notif_tbl Table for notifications to fill
413 *
414 * @return Number of returned notifications
415 *
416 * @see em_event_group_apply(), em_event_group_abort()
417 */
418int em_event_group_notif(em_event_group_t event_group,
419 int max_notif, em_notif_t notif_tbl[]);
420
421/* Backwards compatible naming ("get") */
422#define em_event_group_get_notif em_event_group_notif
423
424/**
425 * Initialize event group iteration and return the first event group handle.
426 *
427 * Can be used to initialize the iteration to retrieve all created event groups
428 * for debugging or management purposes. Use em_event_group_next() after
429 * this call until it returns EM_EVENT_GROUP_UNDEF.
430 * A new call to em_event_group_first() resets the iteration, which is
431 * maintained per core (thread). The operation should be completed in one go
432 * before returning from the EO's event receive function (or start/stop).
433 *
434 * The number of event groups (output arg 'num') may not match the amount of
435 * event groups actually returned by iterating using em_event_group_next()
436 * if event groups are added or removed in parallel by another core. The order
437 * of the returned event group handles is undefined.
438 *
439 * @code
440 * unsigned int num;
441 * em_event_group_t eg = em_event_group_first(&num);
442 * while (eg != EM_EVENT_GROUP_UNDEF) {
443 * eg = em_event_group_next();
444 * }
445 * @endcode
446 *
447 * @param[out] num Pointer to an unsigned int to store the amount of
448 * event groups into
449 * @return The first event group handle or EM_EVENT_GROUP_UNDEF if none exist
450 *
451 * @see em_event_group_next()
452 **/
453em_event_group_t em_event_group_first(unsigned int *num);
454
455/* Backwards compatible naming ("get") */
456#define em_event_group_get_first em_event_group_first
457
458/**
459 * Return the next event group handle.
460 *
461 * Continues the event group iteration started by em_event_group_first() and
462 * returns the next event group handle.
463 *
464 * @return The next event group handle or EM_EVENT_GROUP_UNDEF if the event
465 * group iteration is completed (i.e. no more event groups available).
466 *
467 * @see em_event_group_first()
468 **/
469em_event_group_t em_event_group_next(void);
470
471/* Backwards compatible naming ("get") */
472#define em_event_group_get_next em_event_group_next
473
474/**
475 * Convert an event group handle to an unsigned integer
476 *
477 * @param event_group Event group handle to be converted
478 * @return uint64_t value that can be used to print/display the handle
479 *
480 * @note This routine is intended to be used for diagnostic purposes
481 * to enable applications to e.g. generate a printable value that represents
482 * an em_event_group_t handle.
483 */
484uint64_t em_event_group_to_u64(em_event_group_t event_group);
485
486/**
487 * @}
488 */
489#ifdef __cplusplus
490}
491#endif
492
493#pragma GCC visibility pop
494#endif /* EVENT_MACHINE_EVENT_GROUP_H_ */
uint32_t em_status_t
uint64_t em_event_group_to_u64(em_event_group_t event_group)
em_event_group_t em_event_group_next(void)
void em_event_group_processing_end(void)
em_status_t em_event_group_delete(em_event_group_t event_group)
int em_event_group_is_ready(em_event_group_t event_group)
em_event_group_t em_event_group_create(void)
em_status_t em_event_group_abort(em_event_group_t event_group)
em_status_t em_event_group_apply(em_event_group_t event_group, int count, int num_notif, const em_notif_t notif_tbl[])
em_event_group_t em_event_group_first(unsigned int *num)
int em_send_group_multi(const em_event_t events[], int num, em_queue_t queue, em_event_group_t event_group)
em_event_group_t em_event_group_current(void)
em_status_t em_send_group(em_event_t event, em_queue_t queue, em_event_group_t event_group)
em_status_t em_event_group_increment(int count)
int em_event_group_notif(em_event_group_t event_group, int max_notif, em_notif_t notif_tbl[])
em_status_t em_event_group_assign(em_event_group_t event_group)