Skip to content

Commit c602c08

Browse files
committed
api: claim ownerless upvalue joins
1 parent 789cc2b commit c602c08

4 files changed

Lines changed: 75 additions & 7 deletions

File tree

‎notes/api-debug-claim-cleanup.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ Permanent shape:
8585
slots or value slots, keep the claim through GC-owned field/upvalue
8686
publication, and drop it only after the API's consumed stack slots are
8787
removed. `lua_setfenv` uses a separate nested claim for the thread value case.
88+
- `lua_upvaluejoin()` resume-claims the target state before reading both Lua
89+
function slots, keeps the claim through trace invalidation and release
90+
publication of the joined upvalue pointer, then drops the claim without
91+
changing stack shape.
8892
- Upvalue introspection APIs (`lua_getupvalue` and `lua_upvalueid`) claim the
8993
target state before reading the function slot. `lua_getupvalue` uses the
9094
protected one-slot growth helper and release-publishes the copied upvalue

‎src/lj_api.c‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,9 +1672,14 @@ LUA_API void *lua_upvalueid(lua_State *L, int idx, int n)
16721672

16731673
LUA_API void lua_upvaluejoin(lua_State *L, int idx1, int n1, int idx2, int n2)
16741674
{
1675+
LJStateClaim claim;
1676+
lua_State *errL = api_errstate(L);
16751677
TValue snap1, snap2;
1676-
GCfunc *fn1 = funcV(index2adr_read(L, idx1, &snap1));
1677-
GCfunc *fn2 = funcV(index2adr_read(L, idx2, &snap2));
1678+
GCfunc *fn1, *fn2;
1679+
if (!lj_state_resumeclaim(L, lj_thr_current_id(G(L)), &claim))
1680+
lj_err_callermsg(errL, "thread busy");
1681+
fn1 = funcV(index2adr_read(L, idx1, &snap1));
1682+
fn2 = funcV(index2adr_read(L, idx2, &snap2));
16781683
n1--; n2--;
16791684
lj_checkapi(isluafunc(fn1), "stack slot %d is not a Lua function", idx1);
16801685
lj_checkapi(isluafunc(fn2), "stack slot %d is not a Lua function", idx2);
@@ -1684,11 +1689,12 @@ LUA_API void lua_upvaluejoin(lua_State *L, int idx1, int n1, int idx2, int n2)
16841689
GCobj *uv = func_uvptr_acq(&fn2->l, (uint32_t)n2);
16851690
GCobj *old = func_uvptr_acq(&fn1->l, (uint32_t)n1);
16861691
if (old != uv) {
1687-
api_trace_flush_mutation(L);
1692+
api_trace_flush_mutation_claimed(L, errL, &claim);
16881693
setgcrefrel(fn1->l.uvptr[n1], uv);
16891694
lj_gc_pubobjobj(L, fn1, uv);
16901695
}
16911696
}
1697+
lj_state_dropresumeclaim(&claim);
16921698
}
16931699

16941700
LUALIB_API void *luaL_testudata(lua_State *L, int idx, const char *tname)

‎tests/suites/m5_publication.lua‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,32 @@ END {
11581158
exit 1
11591159
}
11601160
}
1161+
]=], "src/lj_api.c")
1162+
1163+
awk([=[
1164+
BEGIN {
1165+
infn = 0; claim = 0; read1 = 0; read2 = 0
1166+
flush = 0; store = 0; pub = 0; drop = 0
1167+
}
1168+
/^LUA_API void lua_upvaluejoin\(lua_State \*L,/ { infn = 1; next }
1169+
infn && /^}/ { infn = 0; next }
1170+
infn && /lj_state_resumeclaim/ { claim = NR }
1171+
infn && /index2adr_read/ {
1172+
if (!read1) read1 = NR
1173+
else if (!read2) read2 = NR
1174+
}
1175+
infn && /api_trace_flush_mutation_claimed/ { flush = NR }
1176+
infn && /setgcrefrel/ { store = NR }
1177+
infn && /lj_gc_pubobjobj/ { pub = NR }
1178+
infn && /lj_state_dropresumeclaim/ { drop = NR }
1179+
END {
1180+
if (!claim || !read1 || !read2 || !flush || !store || !pub || !drop ||
1181+
claim > read1 || read1 > read2 || read2 > flush || flush > store ||
1182+
store > pub || pub > drop) {
1183+
print "lua_upvaluejoin owner-claim boundary missing"
1184+
exit 1
1185+
}
1186+
}
11611187
]=], "src/lj_api.c")
11621188

11631189
awk([=[

‎tests/t-state-owner.c‎

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,21 @@ static void expect_thread_busy(lua_State *L, lua_CFunction fn,
3737
const char *what)
3838
{
3939
int status;
40+
const char *msg;
4041
lua_pushcfunction(L, fn);
4142
status = lua_pcall(L, 0, 0, 0);
42-
assert(status == LUA_ERRRUN);
43-
assert(lua_tostring(L, -1) != NULL);
44-
assert(strstr(lua_tostring(L, -1), "thread busy") != NULL);
43+
if (status != LUA_ERRRUN) {
44+
fprintf(stderr, "%s: expected LUA_ERRRUN, got %d\n", what, status);
45+
assert(status == LUA_ERRRUN);
46+
}
47+
msg = lua_tostring(L, -1);
48+
if (msg == NULL || strstr(msg, "thread busy") == NULL) {
49+
fprintf(stderr, "%s: unexpected error object type=%s message=%s\n",
50+
what, lua_typename(L, lua_type(L, -1)), msg ? msg : "<null>");
51+
assert(msg != NULL);
52+
assert(strstr(msg, "thread busy") != NULL);
53+
}
4554
lua_pop(L, 1);
46-
(void)what;
4755
}
4856

4957
static uint32_t foreign_tid(lua_State *L)
@@ -455,6 +463,16 @@ static void check_upvalue_api_unowned(lua_State *L)
455463
assert(lua_tointeger(co, -1) == 80);
456464
assert(lj_state_owner_acq(co) == 0);
457465

466+
lua_settop(L, 0);
467+
co = load_ownerless_results(L,
468+
"local function make(x) return function() return x end end\n"
469+
"return make(1), make(2)", 2);
470+
lua_upvaluejoin(co, 1, 1, 2, 1);
471+
lua_pushvalue(co, 1);
472+
lua_call(co, 0, 1);
473+
assert(lua_tointeger(co, -1) == 2);
474+
assert(lj_state_owner_acq(co) == 0);
475+
458476
lua_settop(L, 0);
459477
}
460478

@@ -1189,6 +1207,19 @@ static int busy_lua_setupvalue(lua_State *L)
11891207
return 0;
11901208
}
11911209

1210+
static int busy_lua_upvaluejoin(lua_State *L)
1211+
{
1212+
lua_State *co = lua_newthread(L);
1213+
assert(luaL_loadstring(L,
1214+
"local function make(x) return function() return x end end\n"
1215+
"return make(1), make(2)") == 0);
1216+
lua_call(L, 0, 2);
1217+
lua_xmove(L, co, 2);
1218+
lj_state_owner_rel(co, foreign_tid(L));
1219+
lua_upvaluejoin(co, 1, 1, 2, 1);
1220+
return 0;
1221+
}
1222+
11921223
static lua_State *busy_udata_prepare(lua_State *L)
11931224
{
11941225
lua_State *co;
@@ -1602,6 +1633,7 @@ int main(void)
16021633
expect_thread_busy(L, busy_lua_getupvalue, "busy lua_getupvalue");
16031634
expect_thread_busy(L, busy_lua_upvalueid, "busy lua_upvalueid");
16041635
expect_thread_busy(L, busy_lua_setupvalue, "busy lua_setupvalue");
1636+
expect_thread_busy(L, busy_lua_upvaluejoin, "busy lua_upvaluejoin");
16051637
expect_thread_busy(L, busy_luaL_testudata, "busy luaL_testudata");
16061638
expect_thread_busy(L, busy_luaL_checkudata, "busy luaL_checkudata");
16071639
expect_thread_busy(L, busy_luaL_getmetafield, "busy luaL_getmetafield");

0 commit comments

Comments
 (0)