]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MAJOR: applet: use a separate run queue to maintain list integrity
authorWilly Tarreau <w@1wt.eu>
Fri, 25 Sep 2015 15:56:16 +0000 (17:56 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 25 Sep 2015 16:07:16 +0000 (18:07 +0200)
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

index cc467c49d7525a22697f9a47b094a72de4020d92..42d894d37b2e45f4018b8a6afc3eec4c836c5e47 100644 (file)
 #include <proto/stream_interface.h>
 
 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);
+               }
        }
 }