MEDIUM: lua-thread: Apply lock only if the parent state is the main thread

The goal is to allow execution of one main lua state per thread.

This patch opens the way to addition of a per-thread dedicated lua state.
By passing the hlua we can figure the original state that's been used
and decide to lock or not.
diff --git a/src/hlua.c b/src/hlua.c
index 94bbad7..b93e46d 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -124,14 +124,16 @@
 static int hlua_panic_safe(lua_State *L) { return 0; }
 static int hlua_panic_ljmp(lua_State *L) { WILL_LJMP(longjmp(safe_ljmp_env, 1)); }
 
-#define SET_SAFE_LJMP(__L) \
+#define SET_SAFE_LJMP_L(__L, __HLUA) \
 	({ \
 		int ret; \
-		HA_SPIN_LOCK(LUA_LOCK, &hlua_global_lock); \
+		if ((__HLUA)->state_from == gL.T) \
+			HA_SPIN_LOCK(LUA_LOCK, &hlua_global_lock); \
 		if (setjmp(safe_ljmp_env) != 0) { \
 			lua_atpanic(__L, hlua_panic_safe); \
 			ret = 0; \
-			HA_SPIN_UNLOCK(LUA_LOCK, &hlua_global_lock); \
+			if ((__HLUA)->state_from == gL.T) \
+				HA_SPIN_UNLOCK(LUA_LOCK, &hlua_global_lock); \
 		} else { \
 			lua_atpanic(__L, hlua_panic_ljmp); \
 			ret = 1; \
@@ -142,12 +144,25 @@
 /* If we are the last function catching Lua errors, we
  * must reset the panic function.
  */
-#define RESET_SAFE_LJMP(__L) \
+#define RESET_SAFE_LJMP_L(__L, __HLUA) \
 	do { \
 		lua_atpanic(__L, hlua_panic_safe); \
-		HA_SPIN_UNLOCK(LUA_LOCK, &hlua_global_lock); \
+		if ((__HLUA)->state_from == gL.T) \
+			HA_SPIN_UNLOCK(LUA_LOCK, &hlua_global_lock); \
 	} while(0)
 
+#define SET_SAFE_LJMP(__HLUA) \
+	SET_SAFE_LJMP_L((__HLUA)->T, __HLUA)
+
+#define RESET_SAFE_LJMP(__HLUA) \
+	RESET_SAFE_LJMP_L((__HLUA)->T, __HLUA)
+
+#define SET_SAFE_LJMP_PARENT(__HLUA) \
+	SET_SAFE_LJMP_L((__HLUA)->state_from, __HLUA)
+
+#define RESET_SAFE_LJMP_PARENT(__HLUA) \
+	RESET_SAFE_LJMP_L((__HLUA)->state_from, __HLUA)
+
 /* Applet status flags */
 #define APPLET_DONE     0x01 /* applet processing is done. */
 /* unused: 0x02 */
@@ -1010,30 +1025,30 @@
  */
 int hlua_ctx_init(struct hlua *lua, lua_State *state_from, struct task *task, int already_safe)
 {
-	if (!already_safe) {
-		if (!SET_SAFE_LJMP(state_from)) {
-			lua->Tref = LUA_REFNIL;
-			return 0;
-		}
-	}
 	lua->Mref = LUA_REFNIL;
 	lua->flags = 0;
 	lua->gc_count = 0;
 	lua->wake_time = TICK_ETERNITY;
 	lua->state_from = state_from;
 	LIST_INIT(&lua->com);
+	if (!already_safe) {
+		if (!SET_SAFE_LJMP_PARENT(lua)) {
+			lua->Tref = LUA_REFNIL;
+			return 0;
+		}
+	}
 	lua->T = lua_newthread(state_from);
 	if (!lua->T) {
 		lua->Tref = LUA_REFNIL;
 		if (!already_safe)
-			RESET_SAFE_LJMP(state_from);
+			RESET_SAFE_LJMP_PARENT(lua);
 		return 0;
 	}
 	hlua_sethlua(lua);
 	lua->Tref = luaL_ref(state_from, LUA_REGISTRYINDEX);
 	lua->task = task;
 	if (!already_safe)
-		RESET_SAFE_LJMP(state_from);
+		RESET_SAFE_LJMP_PARENT(lua);
 	return 1;
 }
 
@@ -1052,15 +1067,15 @@
 	/* Purge all the pending signals. */
 	notification_purge(&lua->com);
 
-	if (!SET_SAFE_LJMP(lua->T))
+	if (!SET_SAFE_LJMP(lua))
 		return;
 	luaL_unref(lua->T, LUA_REGISTRYINDEX, lua->Mref);
-	RESET_SAFE_LJMP(lua->T);
+	RESET_SAFE_LJMP(lua);
 
-	if (!SET_SAFE_LJMP(lua->state_from))
+	if (!SET_SAFE_LJMP_PARENT(lua))
 		return;
 	luaL_unref(lua->state_from, LUA_REGISTRYINDEX, lua->Tref);
-	RESET_SAFE_LJMP(lua->state_from);
+	RESET_SAFE_LJMP_PARENT(lua);
 	/* Forces a garbage collecting process. If the Lua program is finished
 	 * without error, we run the GC on the thread pointer. Its freed all
 	 * the unused memory.
@@ -1071,10 +1086,10 @@
 	 * the garbage collection.
 	 */
 	if (lua->gc_count) {
-		if (!SET_SAFE_LJMP(lua->state_from))
+		if (!SET_SAFE_LJMP_PARENT(lua))
 			return;
 		lua_gc(lua->state_from, LUA_GCCOLLECT, 0);
-		RESET_SAFE_LJMP(lua->state_from);
+		RESET_SAFE_LJMP_PARENT(lua);
 	}
 
 	lua->T = NULL;
@@ -1201,7 +1216,8 @@
 	/* Lock the whole Lua execution. This lock must be before the
 	 * label "resume_execution".
 	 */
-	HA_SPIN_LOCK(LUA_LOCK, &hlua_global_lock);
+	if (lua->state_from == gL.T)
+		HA_SPIN_LOCK(LUA_LOCK, &hlua_global_lock);
 
 resume_execution:
 
@@ -1345,7 +1361,8 @@
 	}
 
 	/* This is the main exit point, remove the Lua lock. */
-	HA_SPIN_UNLOCK(LUA_LOCK, &hlua_global_lock);
+	if (lua->state_from == gL.T)
+		HA_SPIN_UNLOCK(LUA_LOCK, &hlua_global_lock);
 
 	return ret;
 }
@@ -6401,7 +6418,7 @@
 	if (!HLUA_IS_RUNNING(stream->hlua)) {
 
 		/* The following Lua calls can fail. */
-		if (!SET_SAFE_LJMP(stream->hlua->T)) {
+		if (!SET_SAFE_LJMP(stream->hlua)) {
 			if (lua_type(stream->hlua->T, -1) == LUA_TSTRING)
 				error = lua_tostring(stream->hlua->T, -1);
 			else
@@ -6413,7 +6430,7 @@
 		/* Check stack available size. */
 		if (!lua_checkstack(stream->hlua->T, 1)) {
 			SEND_ERR(stream->be, "Lua converter '%s': full stack.\n", fcn->name);
-			RESET_SAFE_LJMP(stream->hlua->T);
+			RESET_SAFE_LJMP(stream->hlua);
 			return 0;
 		}
 
@@ -6423,7 +6440,7 @@
 		/* convert input sample and pust-it in the stack. */
 		if (!lua_checkstack(stream->hlua->T, 1)) {
 			SEND_ERR(stream->be, "Lua converter '%s': full stack.\n", fcn->name);
-			RESET_SAFE_LJMP(stream->hlua->T);
+			RESET_SAFE_LJMP(stream->hlua);
 			return 0;
 		}
 		hlua_smp2lua(stream->hlua->T, smp);
@@ -6434,7 +6451,7 @@
 			for (; arg_p->type != ARGT_STOP; arg_p++) {
 				if (!lua_checkstack(stream->hlua->T, 1)) {
 					SEND_ERR(stream->be, "Lua converter '%s': full stack.\n", fcn->name);
-					RESET_SAFE_LJMP(stream->hlua->T);
+					RESET_SAFE_LJMP(stream->hlua);
 					return 0;
 				}
 				hlua_arg2lua(stream->hlua->T, arg_p);
@@ -6446,7 +6463,7 @@
 		stream->hlua->max_time = hlua_timeout_session;
 
 		/* At this point the execution is safe. */
-		RESET_SAFE_LJMP(stream->hlua->T);
+		RESET_SAFE_LJMP(stream->hlua);
 	}
 
 	/* Execute the function. */
@@ -6534,7 +6551,7 @@
 	if (!HLUA_IS_RUNNING(stream->hlua)) {
 
 		/* The following Lua calls can fail. */
-		if (!SET_SAFE_LJMP(stream->hlua->T)) {
+		if (!SET_SAFE_LJMP(stream->hlua)) {
 			if (lua_type(stream->hlua->T, -1) == LUA_TSTRING)
 				error = lua_tostring(stream->hlua->T, -1);
 			else
@@ -6546,7 +6563,7 @@
 		/* Check stack available size. */
 		if (!lua_checkstack(stream->hlua->T, 2)) {
 			SEND_ERR(smp->px, "Lua sample-fetch '%s': full stack.\n", fcn->name);
-			RESET_SAFE_LJMP(stream->hlua->T);
+			RESET_SAFE_LJMP(stream->hlua);
 			return 0;
 		}
 
@@ -6556,7 +6573,7 @@
 		/* push arguments in the stack. */
 		if (!hlua_txn_new(stream->hlua->T, stream, smp->px, smp->opt & SMP_OPT_DIR, hflags)) {
 			SEND_ERR(smp->px, "Lua sample-fetch '%s': full stack.\n", fcn->name);
-			RESET_SAFE_LJMP(stream->hlua->T);
+			RESET_SAFE_LJMP(stream->hlua);
 			return 0;
 		}
 		stream->hlua->nargs = 1;
@@ -6566,7 +6583,7 @@
 			/* Check stack available size. */
 			if (!lua_checkstack(stream->hlua->T, 1)) {
 				SEND_ERR(smp->px, "Lua sample-fetch '%s': full stack.\n", fcn->name);
-				RESET_SAFE_LJMP(stream->hlua->T);
+				RESET_SAFE_LJMP(stream->hlua);
 				return 0;
 			}
 			hlua_arg2lua(stream->hlua->T, arg_p);
@@ -6577,7 +6594,7 @@
 		stream->hlua->max_time = hlua_timeout_session;
 
 		/* At this point the execution is safe. */
-		RESET_SAFE_LJMP(stream->hlua->T);
+		RESET_SAFE_LJMP(stream->hlua);
 	}
 
 	/* Execute the function. */
@@ -6837,7 +6854,7 @@
 	if (!HLUA_IS_RUNNING(s->hlua)) {
 
 		/* The following Lua calls can fail. */
-		if (!SET_SAFE_LJMP(s->hlua->T)) {
+		if (!SET_SAFE_LJMP(s->hlua)) {
 			if (lua_type(s->hlua->T, -1) == LUA_TSTRING)
 				error = lua_tostring(s->hlua->T, -1);
 			else
@@ -6851,7 +6868,7 @@
 		if (!lua_checkstack(s->hlua->T, 1)) {
 			SEND_ERR(px, "Lua function '%s': full stack.\n",
 			         rule->arg.hlua_rule->fcn->name);
-			RESET_SAFE_LJMP(s->hlua->T);
+			RESET_SAFE_LJMP(s->hlua);
 			goto end;
 		}
 
@@ -6862,7 +6879,7 @@
 		if (!hlua_txn_new(s->hlua->T, s, px, dir, hflags)) {
 			SEND_ERR(px, "Lua function '%s': full stack.\n",
 			         rule->arg.hlua_rule->fcn->name);
-			RESET_SAFE_LJMP(s->hlua->T);
+			RESET_SAFE_LJMP(s->hlua);
 			goto end;
 		}
 		s->hlua->nargs = 1;
@@ -6872,7 +6889,7 @@
 			if (!lua_checkstack(s->hlua->T, 1)) {
 				SEND_ERR(px, "Lua function '%s': full stack.\n",
 				         rule->arg.hlua_rule->fcn->name);
-				RESET_SAFE_LJMP(s->hlua->T);
+				RESET_SAFE_LJMP(s->hlua);
 				goto end;
 			}
 			lua_pushstring(s->hlua->T, *arg);
@@ -6880,7 +6897,7 @@
 		}
 
 		/* Now the execution is safe. */
-		RESET_SAFE_LJMP(s->hlua->T);
+		RESET_SAFE_LJMP(s->hlua);
 
 		/* We must initialize the execution timeouts. */
 		s->hlua->max_time = hlua_timeout_session;
@@ -7021,7 +7038,7 @@
 	hlua->max_time = ctx->applet->timeout;
 
 	/* The following Lua calls can fail. */
-	if (!SET_SAFE_LJMP(hlua->T)) {
+	if (!SET_SAFE_LJMP(hlua)) {
 		if (lua_type(hlua->T, -1) == LUA_TSTRING)
 			error = lua_tostring(hlua->T, -1);
 		else
@@ -7035,7 +7052,7 @@
 	if (!lua_checkstack(hlua->T, 1)) {
 		SEND_ERR(px, "Lua applet tcp '%s': full stack.\n",
 		         ctx->rule->arg.hlua_rule->fcn->name);
-		RESET_SAFE_LJMP(hlua->T);
+		RESET_SAFE_LJMP(hlua);
 		return 0;
 	}
 
@@ -7046,7 +7063,7 @@
 	if (!hlua_applet_tcp_new(hlua->T, ctx)) {
 		SEND_ERR(px, "Lua applet tcp '%s': full stack.\n",
 		         ctx->rule->arg.hlua_rule->fcn->name);
-		RESET_SAFE_LJMP(hlua->T);
+		RESET_SAFE_LJMP(hlua);
 		return 0;
 	}
 	hlua->nargs = 1;
@@ -7056,14 +7073,14 @@
 		if (!lua_checkstack(hlua->T, 1)) {
 			SEND_ERR(px, "Lua applet tcp '%s': full stack.\n",
 			         ctx->rule->arg.hlua_rule->fcn->name);
-			RESET_SAFE_LJMP(hlua->T);
+			RESET_SAFE_LJMP(hlua);
 			return 0;
 		}
 		lua_pushstring(hlua->T, *arg);
 		hlua->nargs++;
 	}
 
-	RESET_SAFE_LJMP(hlua->T);
+	RESET_SAFE_LJMP(hlua);
 
 	/* Wakeup the applet ASAP. */
 	si_cant_get(si);
@@ -7214,7 +7231,7 @@
 	hlua->max_time = ctx->applet->timeout;
 
 	/* The following Lua calls can fail. */
-	if (!SET_SAFE_LJMP(hlua->T)) {
+	if (!SET_SAFE_LJMP(hlua)) {
 		if (lua_type(hlua->T, -1) == LUA_TSTRING)
 			error = lua_tostring(hlua->T, -1);
 		else
@@ -7228,7 +7245,7 @@
 	if (!lua_checkstack(hlua->T, 1)) {
 		SEND_ERR(px, "Lua applet http '%s': full stack.\n",
 		         ctx->rule->arg.hlua_rule->fcn->name);
-		RESET_SAFE_LJMP(hlua->T);
+		RESET_SAFE_LJMP(hlua);
 		return 0;
 	}
 
@@ -7239,7 +7256,7 @@
 	if (!hlua_applet_http_new(hlua->T, ctx)) {
 		SEND_ERR(px, "Lua applet http '%s': full stack.\n",
 		         ctx->rule->arg.hlua_rule->fcn->name);
-		RESET_SAFE_LJMP(hlua->T);
+		RESET_SAFE_LJMP(hlua);
 		return 0;
 	}
 	hlua->nargs = 1;
@@ -7249,14 +7266,14 @@
 		if (!lua_checkstack(hlua->T, 1)) {
 			SEND_ERR(px, "Lua applet http '%s': full stack.\n",
 			         ctx->rule->arg.hlua_rule->fcn->name);
-			RESET_SAFE_LJMP(hlua->T);
+			RESET_SAFE_LJMP(hlua);
 			return 0;
 		}
 		lua_pushstring(hlua->T, *arg);
 		hlua->nargs++;
 	}
 
-	RESET_SAFE_LJMP(hlua->T);
+	RESET_SAFE_LJMP(hlua);
 
 	/* Wakeup the applet when data is ready for read. */
 	si_cant_get(si);
@@ -7805,7 +7822,7 @@
 	}
 
 	/* The following Lua calls can fail. */
-	if (!SET_SAFE_LJMP(hlua->T)) {
+	if (!SET_SAFE_LJMP(hlua)) {
 		if (lua_type(hlua->T, -1) == LUA_TSTRING)
 			error = lua_tostring(hlua->T, -1);
 		else
@@ -7847,14 +7864,14 @@
 	hlua->max_time = hlua_timeout_session;
 
 	/* At this point the execution is safe. */
-	RESET_SAFE_LJMP(hlua->T);
+	RESET_SAFE_LJMP(hlua);
 
 	/* It's ok */
 	return 0;
 
 	/* It's not ok. */
 error:
-	RESET_SAFE_LJMP(hlua->T);
+	RESET_SAFE_LJMP(hlua);
 	hlua_ctx_destroy(hlua);
 	appctx->ctx.hlua_cli.hlua = NULL;
 	return 1;