From 9994238adc2c1f1003f72eefe08e610c1baf1bf3 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 25 Sep 2015 17:56:16 +0200 Subject: [PATCH] BUG/MAJOR: applet: use a separate run queue to maintain list integrity If an applet wakes up and causes the next one to sleep, the active list is corrupted and cannot be scanned anymore, as the process then loops over the next element. In order to avoid this problem, we move the active applet list to a run queue and reinit the active list. Only the first element of this queue is checked, and if the element is not removed, it is removed and requeued into the active list. Since we're using a distinct list, if an applet wants to requeue another applet into the active list, it properly gets added to the active list and not to the run queue. This stops the infinite loop issue that could be caused with Lua applets, and in any future configuration where two applets could be attached together. --- src/applet.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/applet.c b/src/applet.c index cc467c49d..42d894d37 100644 --- a/src/applet.c +++ b/src/applet.c @@ -20,13 +20,29 @@ #include struct list applet_active_queue = LIST_HEAD_INIT(applet_active_queue); +struct list applet_run_queue = LIST_HEAD_INIT(applet_run_queue); void applet_run_active() { - struct appctx *curr, *back; + struct appctx *curr; struct stream_interface *si; - list_for_each_entry_safe(curr, back, &applet_active_queue, runq) { + if (LIST_ISEMPTY(&applet_active_queue)) + return; + + /* move active queue to run queue */ + applet_active_queue.n->p = &applet_run_queue; + applet_active_queue.p->n = &applet_run_queue; + + applet_run_queue = applet_active_queue; + LIST_INIT(&applet_active_queue); + + /* The list is only scanned from the head. This guarantees that if any + * applet removes another one, there is no side effect while walking + * through the list. + */ + while (!LIST_ISEMPTY(&applet_run_queue)) { + curr = LIST_ELEM(applet_run_queue.n, typeof(curr), runq); si = curr->owner; /* now we'll need a buffer */ @@ -46,5 +62,11 @@ void applet_run_active() curr->applet->fct(curr); si_applet_done(si); + + if (applet_run_queue.n == &curr->runq) { + /* curr was left in the list, move it back to the active list */ + LIST_DEL(&curr->runq); + LIST_ADDQ(&applet_active_queue, &curr->runq); + } } }