From 17e0c29d9b435392016b707309ed51409b0aea12 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Mon, 15 Jan 2024 11:31:49 -0300 Subject: Clear interface between references and predefines The reference system has a defined way to add initial values to the table where it operates. --- lauxlib.c | 28 ++++++++++++---------------- lstate.c | 3 +++ ltests.c | 28 +++++++++++++++++++++++++--- lua.h | 5 +++-- manual/manual.of | 26 ++++++++++++++++++-------- testes/api.lua | 37 +++++++++++++++++++++++++++---------- testes/coroutine.lua | 8 ++++---- 7 files changed, 92 insertions(+), 43 deletions(-) diff --git a/lauxlib.c b/lauxlib.c index ab3c7c93..8d23bc7d 100644 --- a/lauxlib.c +++ b/lauxlib.c @@ -672,13 +672,10 @@ LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) { ** ======================================================= */ -/* index of free-list header (after the predefined values) */ -#define freelist (LUA_RIDX_LAST + 1) - /* -** The previously freed references form a linked list: -** t[freelist] is the index of a first free index, or zero if list is -** empty; t[t[freelist]] is the index of the second element; etc. +** The previously freed references form a linked list: t[1] is the index +** of a first free index, t[t[1]] is the index of the second element, +** etc. A zero signals the end of the list. */ LUALIB_API int luaL_ref (lua_State *L, int t) { int ref; @@ -687,19 +684,18 @@ LUALIB_API int luaL_ref (lua_State *L, int t) { return LUA_REFNIL; /* 'nil' has a unique fixed reference */ } t = lua_absindex(L, t); - if (lua_rawgeti(L, t, freelist) == LUA_TNIL) { /* first access? */ + if (lua_rawgeti(L, t, 1) == LUA_TNUMBER) /* already initialized? */ + ref = (int)lua_tointeger(L, -1); /* ref = t[1] */ + else { /* first access */ + lua_assert(!lua_toboolean(L, -1)); /* must be nil or false */ ref = 0; /* list is empty */ lua_pushinteger(L, 0); /* initialize as an empty list */ - lua_rawseti(L, t, freelist); /* ref = t[freelist] = 0 */ - } - else { /* already initialized */ - lua_assert(lua_isinteger(L, -1)); - ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */ + lua_rawseti(L, t, 1); /* ref = t[1] = 0 */ } lua_pop(L, 1); /* remove element from stack */ if (ref != 0) { /* any free element? */ lua_rawgeti(L, t, ref); /* remove it from list */ - lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */ + lua_rawseti(L, t, 1); /* (t[1] = t[ref]) */ } else /* no free elements */ ref = (int)lua_rawlen(L, t) + 1; /* get a new reference */ @@ -711,11 +707,11 @@ LUALIB_API int luaL_ref (lua_State *L, int t) { LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { if (ref >= 0) { t = lua_absindex(L, t); - lua_rawgeti(L, t, freelist); + lua_rawgeti(L, t, 1); lua_assert(lua_isinteger(L, -1)); - lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */ + lua_rawseti(L, t, ref); /* t[ref] = t[1] */ lua_pushinteger(L, ref); - lua_rawseti(L, t, freelist); /* t[freelist] = ref */ + lua_rawseti(L, t, 1); /* t[1] = ref */ } } diff --git a/lstate.c b/lstate.c index 78146bdb..9a61cd6d 100644 --- a/lstate.c +++ b/lstate.c @@ -189,6 +189,9 @@ static void init_registry (lua_State *L, global_State *g) { Table *registry = luaH_new(L); sethvalue(L, &g->l_registry, registry); luaH_resize(L, registry, LUA_RIDX_LAST, 0); + /* registry[1] = false */ + setbfvalue(&aux); + luaH_setint(L, registry, 1, &aux); /* registry[LUA_RIDX_MAINTHREAD] = L */ setthvalue(L, &aux, L); luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &aux); diff --git a/ltests.c b/ltests.c index 6eebc732..6de62e52 100644 --- a/ltests.c +++ b/ltests.c @@ -1084,27 +1084,39 @@ static int string_query (lua_State *L) { } +static int getreftable (lua_State *L) { + if (lua_istable(L, 2)) /* is there a table as second argument? */ + return 2; /* use it as the table */ + else + return LUA_REGISTRYINDEX; /* default is to use the register */ +} + + static int tref (lua_State *L) { + int t = getreftable(L); int level = lua_gettop(L); luaL_checkany(L, 1); lua_pushvalue(L, 1); - lua_pushinteger(L, luaL_ref(L, LUA_REGISTRYINDEX)); + lua_pushinteger(L, luaL_ref(L, t)); cast_void(level); /* to avoid warnings */ lua_assert(lua_gettop(L) == level+1); /* +1 for result */ return 1; } + static int getref (lua_State *L) { + int t = getreftable(L); int level = lua_gettop(L); - lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_checkinteger(L, 1)); + lua_rawgeti(L, t, luaL_checkinteger(L, 1)); cast_void(level); /* to avoid warnings */ lua_assert(lua_gettop(L) == level+1); return 1; } static int unref (lua_State *L) { + int t = getreftable(L); int level = lua_gettop(L); - luaL_unref(L, LUA_REGISTRYINDEX, cast_int(luaL_checkinteger(L, 1))); + luaL_unref(L, t, cast_int(luaL_checkinteger(L, 1))); cast_void(level); /* to avoid warnings */ lua_assert(lua_gettop(L) == level); return 0; @@ -1373,6 +1385,16 @@ static int getnum_aux (lua_State *L, lua_State *L1, const char **pc) { (*pc)++; return res; } + else if (**pc == '!') { + (*pc)++; + if (**pc == 'G') + res = LUA_RIDX_GLOBALS; + else if (**pc == 'M') + res = LUA_RIDX_MAINTHREAD; + else lua_assert(0); + (*pc)++; + return res; + } else if (**pc == '-') { sig = -1; (*pc)++; diff --git a/lua.h b/lua.h index 26b45e3e..b7508b4e 100644 --- a/lua.h +++ b/lua.h @@ -80,9 +80,10 @@ typedef struct lua_State lua_State; /* predefined values in the registry */ -#define LUA_RIDX_MAINTHREAD 1 +/* index 1 is reserved for the reference mechanism */ #define LUA_RIDX_GLOBALS 2 -#define LUA_RIDX_LAST LUA_RIDX_GLOBALS +#define LUA_RIDX_MAINTHREAD 3 +#define LUA_RIDX_LAST 3 /* type of numbers in Lua */ diff --git a/manual/manual.of b/manual/manual.of index ae38d7c6..64bb5473 100644 --- a/manual/manual.of +++ b/manual/manual.of @@ -2645,8 +2645,8 @@ string keys starting with an underscore followed by uppercase letters are reserved for Lua. The integer keys in the registry are used -by the reference mechanism @seeC{luaL_ref} -and by some predefined values. +by the reference mechanism @seeC{luaL_ref}, +with some predefined values. Therefore, integer keys in the registry must not be used for other purposes. @@ -6018,11 +6018,21 @@ Creates and returns a @def{reference}, in the table at index @id{t}, for the object on the top of the stack (and pops the object). -A reference is a unique integer key. -As long as you do not manually add integer keys into the table @id{t}, -@Lid{luaL_ref} ensures the uniqueness of the key it returns. +The reference system uses the integer keys of the table. +A reference is a unique integer key; +@Lid{luaL_ref} ensures the uniqueness of the keys it returns. +The entry 1 is reserved for internal use. +Before the first use of @Lid{luaL_ref}, +the integer keys of the table +should form a proper sequence (no holes), +and the value at entry 1 should be false: +@nil if the sequence is empty, +@false otherwise. +You should not manually set integer keys in the table +after the first use of @Lid{luaL_ref}. + You can retrieve an object referred by the reference @id{r} -by calling @T{lua_rawgeti(L, t, r)}. +by calling @T{lua_rawgeti(L, t, r)} or @T{lua_geti(L, t, r)}. The function @Lid{luaL_unref} frees a reference. If the object on the top of the stack is @nil, @@ -6188,8 +6198,8 @@ Returns the name of the type of the value at the given index. Releases the reference @id{ref} from the table at index @id{t} @seeC{luaL_ref}. The entry is removed from the table, -so that the referred object can be collected. -The reference @id{ref} is also freed to be used again. +so that the referred object can be collected and +the reference @id{ref} can be used again by @Lid{luaL_ref}. If @id{ref} is @Lid{LUA_NOREF} or @Lid{LUA_REFNIL}, @Lid{luaL_unref} does nothing. diff --git a/testes/api.lua b/testes/api.lua index 85dadb69..ca4b3fb4 100644 --- a/testes/api.lua +++ b/testes/api.lua @@ -467,7 +467,7 @@ for i = 1,lim do prog[#prog + 1] = "pushnum " .. i * 10 end -prog[#prog + 1] = "rawgeti R 2" -- get global table in registry +prog[#prog + 1] = "rawgeti R !G" -- get global table in registry prog[#prog + 1] = "insert " .. -(2*lim + 2) for i = 1,lim do @@ -930,28 +930,30 @@ checkerr("FILE%* expected, got userdata", io.input, x) assert(debug.getmetatable(x) == nil and debug.getmetatable(y) == nil) -local d = T.ref(a); -local e = T.ref(b); -local f = T.ref(c); -t = {T.getref(d), T.getref(e), T.getref(f)} +-- Test references in an arbitrary table +local reftable = {} +local d = T.ref(a, reftable); +local e = T.ref(b, reftable); +local f = T.ref(c, reftable); +t = {T.getref(d, reftable), T.getref(e, reftable), T.getref(f, reftable)} assert(t[1] == a and t[2] == b and t[3] == c) t=nil; a=nil; c=nil; -T.unref(e); T.unref(f) +T.unref(e, reftable); T.unref(f, reftable) collectgarbage() -- check that unref objects have been collected assert(#cl == 1 and cl[1] == nc) -x = T.getref(d) +x = T.getref(d, reftable) assert(type(x) == 'userdata' and debug.getmetatable(x) == tt) x =nil tt.b = b -- create cycle tt=nil -- frees tt for GC A = nil b = nil -T.unref(d); +T.unref(d, reftable); local n5 = T.newuserdata(0) debug.setmetatable(n5, {__gc=F}) n5 = T.udataval(n5) @@ -960,6 +962,21 @@ assert(#cl == 4) -- check order of collection assert(cl[2] == n5 and cl[3] == nb and cl[4] == na) +-- reuse a reference in 'reftable' +T.unref(T.ref(23, reftable), reftable) + +do -- check reftable + local count = 0 + local i = 1 + while reftable[i] ~= 0 do + i = reftable[i] -- traverse linked list of free references + count = count + 1 + end + -- maximum number of simultaneously locked objects was 3 + assert(count == 3 and #reftable == 3 + 1) -- +1 for reserved [1] +end + + collectgarbage"restart" @@ -1363,8 +1380,8 @@ end) -- testing threads --- get main thread from registry (at index LUA_RIDX_MAINTHREAD == 1) -local mt = T.testC("rawgeti R 1; return 1") +-- get main thread from registry +local mt = T.testC("rawgeti R !M; return 1") assert(type(mt) == "thread" and coroutine.running() == mt) diff --git a/testes/coroutine.lua b/testes/coroutine.lua index 990da8c4..6c15db03 100644 --- a/testes/coroutine.lua +++ b/testes/coroutine.lua @@ -681,7 +681,7 @@ else c == "ERRRUN" and d == 4) a, b, c, d = T.testC([[ - rawgeti R 1 # get main thread + rawgeti R !M # get main thread pushnum 10; pushnum 20; resume -3 2; @@ -699,7 +699,7 @@ else assert(T.testC(state, "newthread; isyieldable -1; remove 1; return 1")) -- main thread is not yieldable - assert(not T.testC(state, "rawgeti R 1; isyieldable -1; remove 1; return 1")) + assert(not T.testC(state, "rawgeti R !M; isyieldable -1; remove 1; return 1")) T.testC(state, "settop 0") @@ -711,7 +711,7 @@ else return 'ok']])) local t = table.pack(T.testC(state, [[ - rawgeti R 1 # get main thread + rawgeti R !M # get main thread pushstring 'XX' getglobal X # get function for body pushstring AA # arg @@ -720,7 +720,7 @@ else setglobal T # top setglobal B # second yielded value setglobal A # fist yielded value - rawgeti R 1 # get main thread + rawgeti R !M # get main thread pushnum 5 # arg (noise) resume 1 1 # after coroutine ends, previous stack is back pushstatus -- cgit v1.2.3-55-g6feb