Skip to content

Commit 73047ea

Browse files
committed
api: claim ownerless C closure pushes
1 parent c088fa6 commit 73047ea

4 files changed

Lines changed: 91 additions & 7 deletions

File tree

notes/api-debug-claim-cleanup.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ Permanent shape:
4444
allocation, then resume-claim to publish the stack slot. `lua_newuserdata()`
4545
and `lua_newthread()` snapshot the target current environment before dropping
4646
the preclaim.
47+
- `lua_pushcclosure()` prechecks the target stack and snapshots the current
48+
environment under a target-state claim, drops that preclaim for closure
49+
allocation, then resume-claims to revalidate the stack, copy upvalues, and
50+
publish the closure result. Zero-upvalue closures use protected one-slot
51+
growth; closures with upvalues replace existing stack slots without requiring
52+
extra stack capacity.
4753
- Public read-only stack getter/conversion APIs (`lua_type`,
4854
`lua_iscfunction`, `lua_isnumber`, `lua_isstring`, `lua_isuserdata`,
4955
`lua_rawequal`, `lua_tonumber`, `lua_tonumberx`, `lua_tointeger`,

src/lj_api.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,19 +1190,33 @@ LUA_API const char *lua_pushfstring(lua_State *L, const char *fmt, ...)
11901190

11911191
LUA_API void lua_pushcclosure(lua_State *L, lua_CFunction f, int n)
11921192
{
1193+
LJStateClaim preclaim, claim;
1194+
lua_State *errL;
1195+
GCtab *env;
11931196
GCfunc *fn;
1194-
lj_gc_check(L);
1197+
int nup = n;
1198+
api_checkclaim(L, &preclaim);
11951199
lj_checkapi_slot(n);
1196-
fn = lj_func_newC(L, (MSize)n, getcurrenv(L));
1200+
env = getcurrenv(L);
1201+
lj_state_dropclaim(&preclaim);
1202+
errL = api_errstate(L);
1203+
fn = lj_func_newC(errL, (MSize)n, env);
11971204
fn->c.f = f;
1198-
L->top -= n;
1199-
while (n--) {
1200-
copyTVrel(L, &fn->c.upvalue[n], L->top+n);
1201-
lj_gc_pubobjtv(L, fn, &fn->c.upvalue[n]);
1205+
if (!lj_state_resumeclaim(L, lj_thr_current_id(G(L)), &claim))
1206+
lj_err_callermsg(errL, "thread busy");
1207+
lj_checkapi_slot(nup);
1208+
if (nup == 0)
1209+
api_checkstack1_claimed(L, errL, &claim);
1210+
L->top -= nup;
1211+
while (nup--) {
1212+
copyTVrel(L, &fn->c.upvalue[nup], L->top+nup);
1213+
lj_gc_pubobjtv(L, fn, &fn->c.upvalue[nup]);
12021214
}
12031215
setfuncV(L, L->top, fn);
1216+
lj_state_stack_pubtv(L, L, L->top);
12041217
lj_assertL(iswhite(obj2gco(fn)), "new GC object is not white");
1205-
incr_top(L);
1218+
L->top++;
1219+
lj_state_dropresumeclaim(&claim);
12061220
}
12071221

12081222
LUA_API void lua_pushboolean(lua_State *L, int b)

tests/suites/m5_publication.lua

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,52 @@ function count_char(text, ch, n, i) {
218218
if (substr(text, i, 1) == ch) n++
219219
return n
220220
}
221+
BEGIN {
222+
infn = 0; started = 0; depth = 0
223+
pre = 0; slot1 = 0; env = 0; pre_drop = 0; alloc = 0; claim = 0
224+
slot2 = 0; zerogrow = 0; grow = 0; copy = 0; store = 0
225+
publish = 0; drop = 0
226+
}
227+
/^LUA_API void lua_pushcclosure\(lua_State \*L,/ { infn = 1; next }
228+
infn {
229+
if (index($0, "{")) started = 1
230+
depth += count_char($0, "{")
231+
depth -= count_char($0, "}")
232+
if (/api_checkclaim\(L, &preclaim\)/) pre = NR
233+
if (/lj_checkapi_slot\(n\)/) slot1 = NR
234+
if (/getcurrenv/) env = NR
235+
if (/lj_state_dropclaim\(&preclaim\)/) pre_drop = NR
236+
if (/lj_func_newC/) alloc = NR
237+
if (/lj_state_resumeclaim/) claim = NR
238+
if (/lj_checkapi_slot\(nup\)/) slot2 = NR
239+
if (/nup == 0/) zerogrow = NR
240+
if (/api_checkstack1_claimed/) grow = NR
241+
if (/copyTVrel\(L, &fn->c.upvalue/) copy = NR
242+
if (/setfuncV/) store = NR
243+
if (/lj_state_stack_pubtv/) publish = NR
244+
if (/lj_state_dropresumeclaim/) drop = NR
245+
if (started && depth == 0) infn = 0
246+
}
247+
END {
248+
if (!pre || !slot1 || !env || !pre_drop || !alloc || !claim || !slot2 ||
249+
!zerogrow || !grow || !copy || !store || !publish || !drop ||
250+
pre > slot1 || slot1 > env || env > pre_drop || pre_drop > alloc ||
251+
alloc > claim || claim > slot2 || slot2 > zerogrow ||
252+
zerogrow > grow || grow > copy || copy > store || store > publish ||
253+
publish > drop) {
254+
print "lua_pushcclosure must snapshot env, allocate unclaimed, and publish claimed"
255+
exit 1
256+
}
257+
}
258+
]=], "src/lj_api.c")
259+
260+
awk([=[
261+
function count_char(text, ch, n, i) {
262+
n = 0
263+
for (i = 1; i <= length(text); i++)
264+
if (substr(text, i, 1) == ch) n++
265+
return n
266+
}
221267
BEGIN {
222268
infn = 0; started = 0; depth = 0
223269
pre = 0; env = 0; pre_drop = 0; alloc = 0; claim = 0; grow = 0

tests/t-state-owner.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,15 @@ static void check_stack_api_unowned(lua_State *L)
131131
assert(lua_gettop(co) == 14 && lua_isuserdata(co, 14));
132132
assert(lua_newthread(co) == lua_tothread(co, 15));
133133
assert(lua_gettop(co) == 15 && lua_type(co, 15) == LUA_TTHREAD);
134+
lua_pushcclosure(co, resume_return, 0);
135+
assert(lua_gettop(co) == 16 && lua_iscfunction(co, 16));
136+
lua_call(co, 0, 1);
137+
assert(lua_gettop(co) == 16 && lua_tointeger(co, 16) == 91);
138+
lua_pushinteger(co, 70);
139+
lua_pushcclosure(co, c_upvalue_return, 1);
140+
assert(lua_gettop(co) == 17 && lua_iscfunction(co, 17));
141+
lua_call(co, 0, 1);
142+
assert(lua_gettop(co) == 17 && lua_tointeger(co, 17) == 70);
134143
lua_settop(co, 3);
135144
lua_settop(co, 5);
136145
assert(lua_gettop(co) == 5);
@@ -740,6 +749,14 @@ static int busy_lua_newthread_api(lua_State *L)
740749
return 0;
741750
}
742751

752+
static int busy_lua_pushcclosure_api(lua_State *L)
753+
{
754+
lua_State *co = lua_newthread(L);
755+
busy_stack_prepare(L, co);
756+
lua_pushcclosure(co, c_upvalue_return, 1);
757+
return 0;
758+
}
759+
743760
static lua_State *busy_getter_prepare(lua_State *L)
744761
{
745762
lua_State *co = lua_newthread(L);
@@ -1377,6 +1394,7 @@ int main(void)
13771394
expect_thread_busy(L, busy_lua_createtable, "busy lua_createtable");
13781395
expect_thread_busy(L, busy_lua_newuserdata, "busy lua_newuserdata");
13791396
expect_thread_busy(L, busy_lua_newthread_api, "busy lua_newthread");
1397+
expect_thread_busy(L, busy_lua_pushcclosure_api, "busy lua_pushcclosure");
13801398
expect_thread_busy(L, busy_lua_type, "busy lua_type");
13811399
expect_thread_busy(L, busy_lua_isnumber, "busy lua_isnumber");
13821400
expect_thread_busy(L, busy_lua_isstring, "busy lua_isstring");

0 commit comments

Comments
 (0)