MAJOR: threads/applet: Handle multithreading for applets

A global lock has been added to protect accesses to the list of active
applets. A process mask has also been added on each applet. Like for FDs and
tasks, it is used to know which threads are allowed to process an
applet. Because applets are, most of time, linked to a session, it should be
sticky on the same thread. But in all cases, it is the responsibility of the
applet handler to lock what have to be protected in the applet context.
diff --git a/src/applet.c b/src/applet.c
index 4e70d8c..b0783e6 100644
--- a/src/applet.c
+++ b/src/applet.c
@@ -23,6 +23,10 @@
 unsigned int nb_applets = 0;
 unsigned int applets_active_queue = 0;
 
+#ifdef USE_THREAD
+HA_SPINLOCK_T applet_active_lock;        /* spin lock related to applet active queue */
+#endif
+
 struct list applet_active_queue = LIST_HEAD_INIT(applet_active_queue);
 
 void applet_run_active()
@@ -34,16 +38,22 @@
 	if (!applets_active_queue)
 		return;
 
+	SPIN_LOCK(APPLETS_LOCK, &applet_active_lock);
+
 	curr = LIST_NEXT(&applet_active_queue, typeof(curr), runq);
 	while (&curr->runq != &applet_active_queue) {
 		next = LIST_NEXT(&curr->runq, typeof(next), runq);
-		LIST_DEL(&curr->runq);
-		curr->state = APPLET_RUNNING;
-		LIST_ADDQ(&applet_cur_queue, &curr->runq);
-		applets_active_queue--;
+		if (curr->process_mask & (1UL << tid)) {
+			LIST_DEL(&curr->runq);
+			curr->state = APPLET_RUNNING;
+			LIST_ADDQ(&applet_cur_queue, &curr->runq);
+			applets_active_queue--;
+		}
 		curr = next;
 	}
 
+	SPIN_UNLOCK(APPLETS_LOCK, &applet_active_lock);
+
 	/* 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.
@@ -74,6 +84,7 @@
 			/* curr was left in the list, move it back to the active list */
 			LIST_DEL(&curr->runq);
 			LIST_INIT(&curr->runq);
+			SPIN_LOCK(APPLETS_LOCK, &applet_active_lock);
 			if (curr->state & APPLET_WANT_DIE) {
 				curr->state = APPLET_SLEEPING;
 				__appctx_free(curr);
@@ -87,6 +98,13 @@
 					curr->state = APPLET_SLEEPING;
 				}
 			}
+			SPIN_UNLOCK(APPLETS_LOCK, &applet_active_lock);
 		}
 	}
 }
+
+__attribute__((constructor))
+static void __applet_init(void)
+{
+	SPIN_INIT(&applet_active_lock);
+}
diff --git a/src/flt_spoe.c b/src/flt_spoe.c
index 9543f8f..aa3f37a 100644
--- a/src/flt_spoe.c
+++ b/src/flt_spoe.c
@@ -1930,7 +1930,7 @@
 	struct session     *sess;
 	struct stream      *strm;
 
-	if ((appctx = appctx_new(&spoe_applet)) == NULL)
+	if ((appctx = appctx_new(&spoe_applet, tid_bit)) == NULL)
 		goto out_error;
 
 	appctx->ctx.spoe.ptr = pool_alloc_dirty(pool2_spoe_appctx);
diff --git a/src/hlua.c b/src/hlua.c
index 36b1b3f..d5d07de 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -2376,7 +2376,7 @@
 	lua_setmetatable(L, -2);
 
 	/* Create the applet context */
-	appctx = appctx_new(&update_applet);
+	appctx = appctx_new(&update_applet, MAX_THREADS_MASK);
 	if (!appctx) {
 		hlua_pusherror(L, "socket: out of memory");
 		goto out_fail_conf;
diff --git a/src/peers.c b/src/peers.c
index 25f1ba3..ef332eb 100644
--- a/src/peers.c
+++ b/src/peers.c
@@ -1839,7 +1839,7 @@
 	peer->statuscode = PEER_SESS_SC_CONNECTCODE;
 	s = NULL;
 
-	appctx = appctx_new(&peer_applet);
+	appctx = appctx_new(&peer_applet, tid_bit);
 	if (!appctx)
 		goto out_close;