Run VM events and finalizers in separate state.

Reported by Sergey Kaplun. #1403
This commit is contained in:
Mike Pall
2025-11-10 18:11:26 +01:00
parent fdf2379ccb
commit 5c647754a6
7 changed files with 76 additions and 67 deletions

View 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--;
}

View 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) \

View 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. */

View 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;
}

View 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;

View 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. */

View 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 \
} \
}