MINOR: event_hdl: normal tasks support for advanced async mode

advanced async mode (EVENT_HDL_ASYNC_TASK) provided full support for
custom tasklets registration.

Due to the similarities between tasks and tasklets, it may be useful
to use the advanced mode with an existing task (not a tasklet).
While the API did not explicitly disallow this usage, things would
get bad if we try to wakeup a task using tasklet_wakeup() for notifying
the task about new events.

To make the API support both custom tasks and tasklets, we use the
TASK_IS_TASKLET() macro to call the proper waking function depending
on the task's type:

  - For tasklets: we use tasklet_wakeup()
  - For tasks: we use task_wakeup()

If 68e692da0 ("MINOR: event_hdl: add event handler base api")
is being backported, then this commit should be backported with it.
This commit is contained in:
Aurelien DARRAGON 2023-02-28 15:06:48 +01:00 committed by Christopher Faulet
parent afcfc20e14
commit b289fd1420
3 changed files with 25 additions and 11 deletions

View File

@ -144,8 +144,8 @@ Registering a handler comes into multiple flavors:
sync mode here is that 'unsafe' data provided
by the data structure may not be used.
task:
handler is a user defined task that uses an event queue
to consume pending events.
handler is a user defined task(let) that uses an event
queue to consume pending events.
This mode is interesting when you need to perform
advanced operations or you need to handle the event
in an already existing task context.
@ -395,9 +395,9 @@ by event_hdl facility to push you events according to your subscription:
```
Then, you need to declare a tasklet (or reuse existing tasklet)
Then, you need to declare a task(let) (or reuse existing task(let))
It is your responsibility to make sure that the tasklet still exists
It is your responsibility to make sure that the task(let) still exists
(is not freed) when calling the subscribe function
(and that the task remains valid as long as the subscription is).
@ -485,9 +485,9 @@ to perform the subscription:
```
Note: it is not recommended to perform multiple subscriptions
that share the same event queue or same tasklet (or both)
that share the same event queue or same task(let) (or both)
That is, having more than one subscription waking a tasklet
That is, having more than one subscription waking a task(let)
and/or feeding the same event queue.
No check is performed on this when registering, so the API

View File

@ -176,7 +176,7 @@ uint64_t event_hdl_id(const char *scope, const char *name);
* to perform subscription lookup by id
* <equeue>: pointer to event_hdl_async_event queue where the pending
* events will be pushed. Cannot be NULL.
* <task>: pointer to tasklet responsible for consuming the events.
* <task>: pointer to task(let) responsible for consuming the events.
* Cannot be NULL.
* <_private>: pointer to private data that will be handled to <func>
* <_private_free>: pointer to 'event_hdl_private_free' prototyped function
@ -187,7 +187,7 @@ uint64_t event_hdl_id(const char *scope, const char *name);
(struct event_hdl){ .id = _id, \
.dorigin = _EVENT_HDL_CALLING_PLACE, \
.async = EVENT_HDL_ASYNC_MODE_ADVANCED, \
.async_task = task, \
.async_task = (struct tasklet *)task, \
.async_equeue = equeue, \
.private = _private, \
.private_free = _private_free }
@ -201,7 +201,7 @@ uint64_t event_hdl_id(const char *scope, const char *name);
*
* <equeue>: pointer to event_hdl_async_event queue where the pending
* events will be pushed. Cannot be NULL.
* <task>: pointer to tasklet responsible for consuming the events
* <task>: pointer to task(let) responsible for consuming the events
* Cannot be NULL.
* <_private>: pointer to private data that will be handled to <func>
* <_private_free>: pointer to 'event_hdl_private_free' prototyped function

View File

@ -196,6 +196,20 @@ void event_hdl_async_free_event(struct event_hdl_async_event *e)
pool_free(pool_head_sub_event, e);
}
/* wakeup the task depending on its type:
* normal async mode internally uses tasklets but advanced async mode
* allows both tasks and tasklets.
* While tasks and tasklets may be easily casted, we need to use the proper
* API to wake them up (the waiting queues are exclusive).
*/
static void event_hdl_task_wakeup(struct tasklet *task)
{
if (TASK_IS_TASKLET(task))
tasklet_wakeup(task);
else
task_wakeup((struct task *)task, TASK_WOKEN_OTHER); /* TODO: switch to TASK_WOKEN_EVENT? */
}
/* task handler used for normal async subscription mode
* if you use advanced async subscription mode, you can use this
* as an example to implement your own task wrapper
@ -306,7 +320,7 @@ static inline void _event_hdl_unsubscribe(struct event_hdl_sub *del_sub)
lock = MT_LIST_APPEND_LOCKED(del_sub->hdl.async_equeue, &del_sub->async_end->mt_list);
/* wake up the task */
tasklet_wakeup(del_sub->hdl.async_task);
event_hdl_task_wakeup(del_sub->hdl.async_task);
/* unlock END EVENT (we're done, the task is now free to consume it) */
MT_LIST_UNLOCK_ELT(&del_sub->async_end->mt_list, lock);
@ -774,7 +788,7 @@ static int _event_hdl_publish(event_hdl_sub_list *sub_list, struct event_hdl_sub
MT_LIST_APPEND(cur_sub->hdl.async_equeue, &new_event->mt_list);
/* wake up the task */
tasklet_wakeup(cur_sub->hdl.async_task);
event_hdl_task_wakeup(cur_sub->hdl.async_task);
} /* end async mode */
} /* end hdl should be notified */
} /* end mt_list */