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.
diff --git a/doc/internals/api/event_hdl.txt b/doc/internals/api/event_hdl.txt
index 4414bc4..102aea4 100644
--- a/doc/internals/api/event_hdl.txt
+++ b/doc/internals/api/event_hdl.txt
@@ -144,8 +144,8 @@
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 @@
```
-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 @@
```
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
diff --git a/include/haproxy/event_hdl.h b/include/haproxy/event_hdl.h
index 097f881..2770d1d 100644
--- a/include/haproxy/event_hdl.h
+++ b/include/haproxy/event_hdl.h
@@ -176,7 +176,7 @@
* 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 @@
(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 @@
*
* <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
diff --git a/src/event_hdl.c b/src/event_hdl.c
index a9fa93a..37a4adc 100644
--- a/src/event_hdl.c
+++ b/src/event_hdl.c
@@ -196,6 +196,20 @@
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 @@
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 @@
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 */