]> git.ipfire.org Git - thirdparty/LuaJIT.git/commitdiff
Run VM events and finalizers in separate state.
authorMike Pall <mike>
Mon, 10 Nov 2025 17:11:26 +0000 (18:11 +0100)
committerMike Pall <mike>
Mon, 10 Nov 2025 17:11:26 +0000 (18:11 +0100)
Reported by Sergey Kaplun. #1403

src/lj_gc.c
src/lj_obj.h
src/lj_parse.c
src/lj_state.c
src/lj_trace.c
src/lj_vmevent.c
src/lj_vmevent.h

index d9581d20d337ab0a39cd252c442d37b2b86f136b..c779d583e98d5198e0040bcdb88b8dc396125490 100644 (file)
@@ -106,6 +106,7 @@ static void gc_mark_start(global_State *g)
   setgcrefnull(g->gc.weak);
   gc_markobj(g, mainthread(g));
   gc_markobj(g, tabref(mainthread(g)->env));
+  gc_markobj(g, vmthread(g));
   gc_marktv(g, &g->registrytv);
   gc_mark_gcroot(g);
   g->gc.state = GCSpropagate;
@@ -507,24 +508,25 @@ static void gc_call_finalizer(global_State *g, lua_State *L,
   uint8_t oldh = hook_save(g);
   GCSize oldt = g->gc.threshold;
   int errcode;
+  lua_State *VL = vmthread(g);
   TValue *top;
   lj_trace_abort(g);
   hook_entergc(g);  /* Disable hooks and new traces during __gc. */
   if (LJ_HASPROFILE && (oldh & HOOK_PROFILE)) lj_dispatch_update(g);
   g->gc.threshold = LJ_MAX_MEM;  /* Prevent GC steps. */
-  top = L->top;
-  copyTV(L, top++, mo);
+  top = VL->top;
+  copyTV(VL, top++, mo);
   if (LJ_FR2) setnilV(top++);
-  setgcV(L, top, o, ~o->gch.gct);
-  L->top = top+1;
-  errcode = lj_vm_pcall(L, top, 1+0, -1);  /* Stack: |mo|o| -> | */
+  setgcV(VL, top, o, ~o->gch.gct);
+  VL->top = top+1;
+  errcode = lj_vm_pcall(VL, top, 1+0, -1);  /* Stack: |mo|o| -> | */
+  setgcref(g->cur_L, obj2gco(L));
   hook_restore(g, oldh);
   if (LJ_HASPROFILE && (oldh & HOOK_PROFILE)) lj_dispatch_update(g);
   g->gc.threshold = oldt;  /* Restore GC threshold. */
   if (errcode) {
-    ptrdiff_t errobj = savestack(L, L->top-1);  /* Stack may be resized. */
-    lj_vmevent_send(L, ERRFIN,
-      copyTV(L, L->top++, restorestack(L, errobj));
+    lj_vmevent_send(g, ERRFIN,
+      copyTV(V, V->top++, L->top-1);
     );
     L->top--;
   }
index 855727bfab601029e42e204a0d39fa82277dc954..73b186e2563e5018fc1b45569d4f1e56a6baa977 100644 (file)
@@ -647,6 +647,7 @@ typedef struct global_State {
   TValue tmptv, tmptv2;        /* Temporary TValues. */
   Node nilnode;                /* Fallback 1-element hash part (nil key and value). */
   TValue registrytv;   /* Anchor for registry. */
+  GCRef vmthref;       /* Link to VM thread. */
   GCupval uvhead;      /* Head of double-linked list of all open upvalues. */
   int32_t hookcount;   /* Instruction hook countdown. */
   int32_t hookcstart;  /* Start count for instruction hook counter. */
@@ -663,6 +664,7 @@ typedef struct global_State {
 } global_State;
 
 #define mainthread(g)  (&gcref(g->mainthref)->th)
+#define vmthread(g)    (&gcref(g->vmthref)->th)
 #define niltv(L) \
   check_exp(tvisnil(&G(L)->nilnode.val), &G(L)->nilnode.val)
 #define niltvg(g) \
index e326432abb4490ac3c53403868caa568fcf5425b..181ce4d7e28c9f2c8cfb8189f8cb800e2b68a244 100644 (file)
@@ -1593,8 +1593,8 @@ static GCproto *fs_finish(LexState *ls, BCLine line)
   fs_fixup_line(fs, pt, (void *)((char *)pt + ofsli), numline);
   fs_fixup_var(ls, pt, (uint8_t *)((char *)pt + ofsdbg), ofsvar);
 
-  lj_vmevent_send(L, BC,
-    setprotoV(L, L->top++, pt);
+  lj_vmevent_send(G(L), BC,
+    setprotoV(V, V->top++, pt);
   );
 
   L->top--;  /* Pop table of constants. */
index fb6d41a5f9bcb61cf8849dda48370fd25e5f2c0a..9d4fdcee3aadf874c4c571927d69a8507acf769d 100644 (file)
@@ -202,6 +202,7 @@ static TValue *cpluaopen(lua_State *L, lua_CFunction dummy, void *ud)
 #endif
   lj_trace_initstate(g);
   lj_err_verify();
+  setgcref(g->vmthref, obj2gco(lj_state_new(L)));
   return NULL;
 }
 
index 3e2cd0b3931dada40bfa0f6007a2fecccee564c1..47d7faa5c9cedd53a929be22fd4abc914dac1f44 100644 (file)
@@ -296,8 +296,8 @@ int lj_trace_flushall(lua_State *L)
   /* Free the whole machine code and invalidate all exit stub groups. */
   lj_mcode_free(J);
   memset(J->exitstubgroup, 0, sizeof(J->exitstubgroup));
-  lj_vmevent_send(L, TRACE,
-    setstrV(L, L->top++, lj_str_newlit(L, "flush"));
+  lj_vmevent_send(J2G(J), TRACE,
+    setstrV(V, V->top++, lj_str_newlit(V, "flush"));
   );
   return 0;
 }
@@ -416,7 +416,6 @@ setpenalty:
 /* Start tracing. */
 static void trace_start(jit_State *J)
 {
-  lua_State *L;
   TraceNo traceno;
 
   if ((J->pt->flags & PROTO_NOJIT)) {  /* JIT disabled for this proto? */
@@ -466,20 +465,19 @@ static void trace_start(jit_State *J)
   J->ktrace = 0;
   setgcref(J->cur.startpt, obj2gco(J->pt));
 
-  L = J->L;
-  lj_vmevent_send(L, TRACE,
-    setstrV(L, L->top++, lj_str_newlit(L, "start"));
-    setintV(L->top++, traceno);
-    setfuncV(L, L->top++, J->fn);
-    setintV(L->top++, proto_bcpos(J->pt, J->pc));
+  lj_vmevent_send(J2G(J), TRACE,
+    setstrV(V, V->top++, lj_str_newlit(V, "start"));
+    setintV(V->top++, traceno);
+    setfuncV(V, V->top++, J->fn);
+    setintV(V->top++, proto_bcpos(J->pt, J->pc));
     if (J->parent) {
-      setintV(L->top++, J->parent);
-      setintV(L->top++, J->exitno);
+      setintV(V->top++, J->parent);
+      setintV(V->top++, J->exitno);
     } else {
       BCOp op = bc_op(*J->pc);
       if (op == BC_CALLM || op == BC_CALL || op == BC_ITERC) {
-       setintV(L->top++, J->exitno);  /* Parent of stitched trace. */
-       setintV(L->top++, -1);
+       setintV(V->top++, J->exitno);  /* Parent of stitched trace. */
+       setintV(V->top++, -1);
       }
     }
   );
@@ -494,7 +492,6 @@ static void trace_stop(jit_State *J)
   GCproto *pt = &gcref(J->cur.startpt)->pt;
   TraceNo traceno = J->cur.traceno;
   GCtrace *T = J->curfinal;
-  lua_State *L;
 
   switch (op) {
   case BC_FORL:
@@ -551,11 +548,10 @@ static void trace_stop(jit_State *J)
   J->postproc = LJ_POST_NONE;
   trace_save(J, T);
 
-  L = J->L;
-  lj_vmevent_send(L, TRACE,
-    setstrV(L, L->top++, lj_str_newlit(L, "stop"));
-    setintV(L->top++, traceno);
-    setfuncV(L, L->top++, J->fn);
+  lj_vmevent_send(J2G(J), TRACE,
+    setstrV(V, V->top++, lj_str_newlit(V, "stop"));
+    setintV(V->top++, traceno);
+    setfuncV(V, V->top++, J->fn);
   );
 }
 
@@ -610,18 +606,17 @@ static int trace_abort(jit_State *J)
   /* Is there anything to abort? */
   traceno = J->cur.traceno;
   if (traceno) {
-    ptrdiff_t errobj = savestack(L, L->top-1);  /* Stack may be resized. */
     J->cur.link = 0;
     J->cur.linktype = LJ_TRLINK_NONE;
-    lj_vmevent_send(L, TRACE,
+    lj_vmevent_send(J2G(J), TRACE,
       cTValue *bot = tvref(L->stack)+LJ_FR2;
       cTValue *frame;
       const BCIns *pc;
       BCPos pos = 0;
-      setstrV(L, L->top++, lj_str_newlit(L, "abort"));
-      setintV(L->top++, traceno);
+      setstrV(V, V->top++, lj_str_newlit(V, "abort"));
+      setintV(V->top++, traceno);
       /* Find original Lua function call to generate a better error message. */
-      for (frame = J->L->base-1, pc = J->pc; ; frame = frame_prev(frame)) {
+      for (frame = L->base-1, pc = J->pc; ; frame = frame_prev(frame)) {
        if (isluafunc(frame_func(frame))) {
          pos = proto_bcpos(funcproto(frame_func(frame)), pc);
          break;
@@ -633,10 +628,10 @@ static int trace_abort(jit_State *J)
          pc = frame_pc(frame) - 1;
        }
       }
-      setfuncV(L, L->top++, frame_func(frame));
-      setintV(L->top++, pos);
-      copyTV(L, L->top++, restorestack(L, errobj));
-      copyTV(L, L->top++, &J->errinfo);
+      setfuncV(V, V->top++, frame_func(frame));
+      setintV(V->top++, pos);
+      copyTV(V, V->top++, L->top-1);
+      copyTV(V, V->top++, &J->errinfo);
     );
     /* Drop aborted trace after the vmevent (which may still access it). */
     setgcrefnull(J->trace[traceno]);
@@ -692,16 +687,16 @@ static TValue *trace_state(lua_State *L, lua_CFunction dummy, void *ud)
     case LJ_TRACE_RECORD:
       trace_pendpatch(J, 0);
       setvmstate(J2G(J), RECORD);
-      lj_vmevent_send_(L, RECORD,
+      lj_vmevent_send_(J2G(J), RECORD,
        /* Save/restore state for trace recorder. */
        TValue savetv = J2G(J)->tmptv;
        TValue savetv2 = J2G(J)->tmptv2;
        TraceNo parent = J->parent;
        ExitNo exitno = J->exitno;
-       setintV(L->top++, J->cur.traceno);
-       setfuncV(L, L->top++, J->fn);
-       setintV(L->top++, J->pt ? (int32_t)proto_bcpos(J->pt, J->pc) : -1);
-       setintV(L->top++, J->framedepth);
+       setintV(V->top++, J->cur.traceno);
+       setfuncV(V, V->top++, J->fn);
+       setintV(V->top++, J->pt ? (int32_t)proto_bcpos(J->pt, J->pc) : -1);
+       setintV(V->top++, J->framedepth);
       ,
        J2G(J)->tmptv = savetv;
        J2G(J)->tmptv2 = savetv2;
@@ -839,23 +834,23 @@ static TValue *trace_exit_cp(lua_State *L, lua_CFunction dummy, void *ud)
 
 #ifndef LUAJIT_DISABLE_VMEVENT
 /* Push all registers from exit state. */
-static void trace_exit_regs(lua_State *L, ExitState *ex)
+static void trace_exit_regs(lua_State *V, ExitState *ex)
 {
   int32_t i;
-  setintV(L->top++, RID_NUM_GPR);
-  setintV(L->top++, RID_NUM_FPR);
+  setintV(V->top++, RID_NUM_GPR);
+  setintV(V->top++, RID_NUM_FPR);
   for (i = 0; i < RID_NUM_GPR; i++) {
     if (sizeof(ex->gpr[i]) == sizeof(int32_t))
-      setintV(L->top++, (int32_t)ex->gpr[i]);
+      setintV(V->top++, (int32_t)ex->gpr[i]);
     else
-      setnumV(L->top++, (lua_Number)ex->gpr[i]);
+      setnumV(V->top++, (lua_Number)ex->gpr[i]);
   }
 #if !LJ_SOFTFP
   for (i = 0; i < RID_NUM_FPR; i++) {
-    setnumV(L->top, ex->fpr[i]);
-    if (LJ_UNLIKELY(tvisnan(L->top)))
-      setnanV(L->top);
-    L->top++;
+    setnumV(V->top, ex->fpr[i]);
+    if (LJ_UNLIKELY(tvisnan(V->top)))
+      setnanV(V->top);
+    V->top++;
   }
 #endif
 }
@@ -897,6 +892,8 @@ int LJ_FASTCALL lj_trace_exit(jit_State *J, void *exptr)
 
 #ifdef EXITSTATE_PCREG
   J->parent = trace_exit_find(J, (MCode *)(intptr_t)ex->gpr[EXITSTATE_PCREG]);
+#else
+  UNUSED(ex);
 #endif
   T = traceref(J, J->parent); UNUSED(T);
 #ifdef EXITSTATE_CHECKEXIT
@@ -917,11 +914,11 @@ int LJ_FASTCALL lj_trace_exit(jit_State *J, void *exptr)
   if (exitcode) copyTV(L, L->top++, &exiterr);  /* Anchor the error object. */
 
   if (!(LJ_HASPROFILE && (G(L)->hookmask & HOOK_PROFILE)))
-    lj_vmevent_send(L, TEXIT,
-      lj_state_checkstack(L, 4+RID_NUM_GPR+RID_NUM_FPR+LUA_MINSTACK);
-      setintV(L->top++, J->parent);
-      setintV(L->top++, J->exitno);
-      trace_exit_regs(L, ex);
+    lj_vmevent_send(G(L), TEXIT,
+      lj_state_checkstack(V, 4+RID_NUM_GPR+RID_NUM_FPR+LUA_MINSTACK);
+      setintV(V->top++, J->parent);
+      setintV(V->top++, J->exitno);
+      trace_exit_regs(V, ex);
     );
 
   pc = exd.pc;
index 070c6144aab43c659829f53172c5042d45954206..8913ead946e621ad33298a6f6db6c0fd3470f8dc 100644 (file)
@@ -38,6 +38,7 @@ ptrdiff_t lj_vmevent_prepare(lua_State *L, VMEvent ev)
 void lj_vmevent_call(lua_State *L, ptrdiff_t argbase)
 {
   global_State *g = G(L);
+  lua_State *oldL = gco2th(gcref(g->cur_L));
   uint8_t oldmask = g->vmevmask;
   uint8_t oldh = hook_save(g);
   int status;
@@ -51,6 +52,10 @@ void lj_vmevent_call(lua_State *L, ptrdiff_t argbase)
     fputs(tvisstr(L->top) ? strVdata(L->top) : "?", stderr);
     fputc('\n', stderr);
   }
+  setgcref(g->cur_L, obj2gco(oldL));
+#if LJ_HASJIT
+  G2J(g)->L = oldL;
+#endif
   hook_restore(g, oldh);
   if (g->vmevmask != VMEVENT_NOCACHE)
     g->vmevmask = oldmask;  /* Restore event mask, but not if not modified. */
index 8a99536068fd935cf8049dd6d69a8aebddefa326..cdd4f75825d0978845e931fbf5ebce395b448f64 100644 (file)
@@ -32,23 +32,25 @@ typedef enum {
 } VMEvent;
 
 #ifdef LUAJIT_DISABLE_VMEVENT
-#define lj_vmevent_send(L, ev, args)           UNUSED(L)
-#define lj_vmevent_send_(L, ev, args, post)    UNUSED(L)
+#define lj_vmevent_send(g, ev, args)           UNUSED(g)
+#define lj_vmevent_send_(g, ev, args, post)    UNUSED(g)
 #else
-#define lj_vmevent_send(L, ev, args) \
-  if (G(L)->vmevmask & VMEVENT_MASK(LJ_VMEVENT_##ev)) { \
-    ptrdiff_t argbase = lj_vmevent_prepare(L, LJ_VMEVENT_##ev); \
+#define lj_vmevent_send(g, ev, args) \
+  if ((g)->vmevmask & VMEVENT_MASK(LJ_VMEVENT_##ev)) { \
+    lua_State *V = vmthread(g); \
+    ptrdiff_t argbase = lj_vmevent_prepare(V, LJ_VMEVENT_##ev); \
     if (argbase) { \
       args \
-      lj_vmevent_call(L, argbase); \
+      lj_vmevent_call(V, argbase); \
     } \
   }
-#define lj_vmevent_send_(L, ev, args, post) \
-  if (G(L)->vmevmask & VMEVENT_MASK(LJ_VMEVENT_##ev)) { \
-    ptrdiff_t argbase = lj_vmevent_prepare(L, LJ_VMEVENT_##ev); \
+#define lj_vmevent_send_(g, ev, args, post) \
+  if ((g)->vmevmask & VMEVENT_MASK(LJ_VMEVENT_##ev)) { \
+    lua_State *V = vmthread(g); \
+    ptrdiff_t argbase = lj_vmevent_prepare(V, LJ_VMEVENT_##ev); \
     if (argbase) { \
       args \
-      lj_vmevent_call(L, argbase); \
+      lj_vmevent_call(V, argbase); \
       post \
     } \
   }